xref: /dragonfly/sys/netproto/mpls/mpls_output.c (revision d4ef6694)
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  * 3. Neither the name of The DragonFly Project nor the names of its
15  *    contributors may be used to endorse or promote products derived
16  *    from this software without specific, prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $DragonFly: src/sys/netproto/mpls/mpls_output.c,v 1.2 2008/08/05 15:11:32 nant Exp $
32  */
33 
34 #include <sys/param.h>
35 #include <sys/mbuf.h>
36 #include <sys/systm.h>
37 
38 #include <net/if_var.h>
39 
40 #include <netinet/ip.h>
41 
42 #include <netproto/mpls/mpls.h>
43 #include <netproto/mpls/mpls_var.h>
44 
45 static int mpls_push(struct mbuf **, mpls_label_t,
46 		     mpls_s_t, mpls_exp_t, mpls_ttl_t);
47 static int mpls_swap(struct mbuf *, mpls_label_t);
48 static int mpls_pop(struct mbuf *, mpls_s_t *);
49 
50 int
51 mpls_output(struct mbuf *m, struct rtentry *rt)
52 {
53 	struct sockaddr_mpls *smpls = NULL;
54 	int error = 0, i;
55 	mpls_s_t stackempty;
56 	mpls_ttl_t ttl = 255;
57 	struct ip *ip;
58 
59 	M_ASSERTPKTHDR(m);
60 
61 	/*
62 	 * Check if we are coming from an MPLS routing table lookup.
63 	 * The rt_key of this rtentry will have a family AF_MPLS if so.
64 	 */
65 	stackempty = rt_key(rt)->sa_family != AF_MPLS ? 1 : 0;
66 	if (stackempty) {
67 		switch (rt_key(rt)->sa_family) {
68 		case AF_INET:
69 			ip = mtod(m, struct ip *);
70 			ttl = ip->ip_ttl;
71 			break;
72 		}
73 	}
74 
75 	for (i=0; i < MPLS_MAXLOPS && rt->rt_shim[i] != NULL; ++i) {
76 		smpls = (struct sockaddr_mpls *)rt->rt_shim[i];
77 		switch (smpls->smpls_op) {
78 		case MPLSLOP_PUSH:
79 			error = mpls_push(&m,
80 				  ntohl(smpls->smpls_label),
81 				  /*
82 				   * If we are the first label push, then
83 				   * set the bottom-of-stack bit.
84 				   */
85 				  (stackempty && i == 0) ? 1 : 0,
86 				  0,
87 				  ttl);
88 			if (error)
89 				return (error);
90 			stackempty = 0;
91 			m->m_flags |= M_MPLSLABELED;
92 			break;
93 		case MPLSLOP_SWAP:
94 			/*
95 			 * Operation is only permmited if label stack
96 			 * is not empty.
97 			 */
98 			if (stackempty)
99 				return (ENOTSUP);
100 			KKASSERT(m->m_flags & M_MPLSLABELED);
101 			error = mpls_swap(m, ntohl(smpls->smpls_label));
102 			if (error)
103 				return (error);
104 			break;
105 		case MPLSLOP_POP:
106 			/*
107 			 * Operation is only permmited if label stack
108 			 * is not empty.
109 			 */
110 			if (stackempty)
111 				return (ENOTSUP);
112 			KKASSERT(m->m_flags & M_MPLSLABELED);
113 			error = mpls_pop(m, &stackempty);
114 			if (error)
115 				return (error);
116 			/*
117 			 * If we are popping out the last label then
118 			 * mark the mbuf as ~M_MPLSLABELED.
119 			 */
120 			if (stackempty)
121 				m->m_flags &= ~M_MPLSLABELED;
122 			break;
123 		default:
124 			/* Unknown label operation */
125 			return (ENOTSUP);
126 		}
127 	}
128 
129 	return (error);
130 }
131 
132 /*
133  * Returns FALSE if no further output processing required.
134  */
135 boolean_t
136 mpls_output_process(struct mbuf *m, struct rtentry *rt)
137 {
138 	int error;
139 
140 	/* Does this route have MPLS label operations? */
141 	if (!(rt->rt_flags & RTF_MPLSOPS))
142 		return TRUE;
143 
144 	error = mpls_output(m, rt);
145 	if (error) {
146 		m_freem(m);
147 		return FALSE;
148 	}
149 
150 	return TRUE;
151 }
152 
153 static int
154 mpls_push(struct mbuf **m, mpls_label_t label, mpls_s_t s, mpls_exp_t exp, mpls_ttl_t ttl) {
155 	struct mpls *mpls;
156 	u_int32_t buf = 0;	/* Silence warning */
157 
158 	M_PREPEND(*m, sizeof(struct mpls), MB_DONTWAIT);
159 	if (*m == NULL)
160 		return (ENOBUFS);
161 
162 	MPLS_SET_LABEL(buf, label);
163 	MPLS_SET_STACK(buf, s);
164 	MPLS_SET_EXP(buf, exp);
165 	MPLS_SET_TTL(buf, ttl);
166 	mpls = mtod(*m, struct mpls *);
167 	mpls->mpls_shim = htonl(buf);
168 
169 	return (0);
170 }
171 
172 static int
173 mpls_swap(struct mbuf *m, mpls_label_t label) {
174 	struct mpls *mpls;
175 	u_int32_t buf;
176 	mpls_ttl_t ttl;
177 
178 	if (m->m_len < sizeof(struct mpls) &&
179 	   (m = m_pullup(m, sizeof(struct mpls))) == NULL)
180 		return (ENOBUFS);
181 
182 	mpls = mtod(m, struct mpls *);
183 	buf = ntohl(mpls->mpls_shim);
184 	ttl = MPLS_TTL(buf);
185 	if (--ttl <= 0) {
186 		/* XXX: should send icmp ttl expired. */
187 		mplsstat.mplss_ttlexpired++;
188 		return (ETIMEDOUT);
189 	}
190 	MPLS_SET_LABEL(buf, label);
191 	MPLS_SET_TTL(buf, ttl); /* XXX tunnel mode: uniform, pipe, short pipe */
192 	mpls->mpls_shim = htonl(buf);
193 
194 	return (0);
195 }
196 
197 static int
198 mpls_pop(struct mbuf *m, mpls_s_t *sbit) {
199 	struct mpls *mpls;
200 	u_int32_t buf;
201 
202 	if (m->m_len < sizeof(struct mpls)) {
203 		m = m_pullup(m, sizeof(struct mpls));
204 		if (m == NULL)
205 			return (ENOBUFS);
206 	}
207 	mpls = mtod(m, struct mpls *);
208 	buf = ntohl(mpls->mpls_shim);
209 	*sbit = MPLS_STACK(buf);
210 
211 	m_adj(m, sizeof(struct mpls));
212 
213 	return (0);
214 }
215