Line data Source code
1 : /* $OpenBSD: mpls_input.c,v 1.68 2018/01/12 06:57:56 jca Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #include "mpe.h"
20 :
21 : #include <sys/param.h>
22 : #include <sys/mbuf.h>
23 : #include <sys/systm.h>
24 : #include <sys/socket.h>
25 :
26 : #include <net/if.h>
27 : #include <net/if_var.h>
28 : #include <net/if_types.h>
29 : #include <net/netisr.h>
30 : #include <net/route.h>
31 :
32 : #include <netinet/in.h>
33 : #include <netinet/ip.h>
34 : #include <netinet/ip_var.h>
35 : #include <netinet/ip_icmp.h>
36 :
37 : #ifdef INET6
38 : #include <netinet/ip6.h>
39 : #endif /* INET6 */
40 :
41 : #include <netmpls/mpls.h>
42 :
43 : #ifdef MPLS_DEBUG
44 : #define MPLS_LABEL_GET(l) ((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
45 : #define MPLS_TTL_GET(l) (ntohl((l) & MPLS_TTL_MASK))
46 : #endif
47 :
48 : struct mbuf *mpls_ip_adjttl(struct mbuf *, u_int8_t);
49 : #ifdef INET6
50 : struct mbuf *mpls_ip6_adjttl(struct mbuf *, u_int8_t);
51 : #endif
52 :
53 : struct mbuf *mpls_do_error(struct mbuf *, int, int, int);
54 :
55 : void
56 0 : mpls_input(struct ifnet *ifp, struct mbuf *m)
57 : {
58 : struct sockaddr_mpls *smpls;
59 0 : struct sockaddr_mpls sa_mpls;
60 : struct shim_hdr *shim;
61 : struct rtentry *rt;
62 : struct rt_mpls *rt_mpls;
63 : uint8_t ttl;
64 : int hasbos;
65 :
66 0 : if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
67 0 : m_freem(m);
68 0 : return;
69 : }
70 :
71 : /* drop all broadcast and multicast packets */
72 0 : if (m->m_flags & (M_BCAST | M_MCAST)) {
73 0 : m_freem(m);
74 0 : return;
75 : }
76 :
77 0 : if (m->m_len < sizeof(*shim)) {
78 0 : m = m_pullup(m, sizeof(*shim));
79 0 : if (m == NULL)
80 0 : return;
81 : }
82 :
83 0 : shim = mtod(m, struct shim_hdr *);
84 : #ifdef MPLS_DEBUG
85 : printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n",
86 : ifp->if_xname, MPLS_LABEL_GET(shim->shim_label),
87 : MPLS_TTL_GET(shim->shim_label),
88 : MPLS_BOS_ISSET(shim->shim_label));
89 : #endif
90 :
91 : /* check and decrement TTL */
92 0 : ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
93 0 : if (--ttl == 0) {
94 : /* TTL exceeded */
95 0 : m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0);
96 0 : if (m == NULL)
97 0 : return;
98 :
99 0 : shim = mtod(m, struct shim_hdr *);
100 0 : ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
101 0 : }
102 0 : hasbos = MPLS_BOS_ISSET(shim->shim_label);
103 :
104 0 : bzero(&sa_mpls, sizeof(sa_mpls));
105 : smpls = &sa_mpls;
106 0 : smpls->smpls_family = AF_MPLS;
107 0 : smpls->smpls_len = sizeof(*smpls);
108 0 : smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
109 :
110 0 : if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) {
111 0 : m = mpls_shim_pop(m);
112 0 : if (m == NULL)
113 0 : return;
114 0 : if (!hasbos) {
115 : /*
116 : * RFC 4182 relaxes the position of the
117 : * explicit NULL labels. They no longer need
118 : * to be at the beginning of the stack.
119 : * In this case the label is ignored and the decision
120 : * is made based on the lower one.
121 : */
122 0 : shim = mtod(m, struct shim_hdr *);
123 0 : smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
124 0 : hasbos = MPLS_BOS_ISSET(shim->shim_label);
125 : } else {
126 0 : switch (ntohl(smpls->smpls_label)) {
127 : case MPLS_LABEL_IPV4NULL:
128 : do_v4:
129 0 : if ((m = mpls_ip_adjttl(m, ttl)) == NULL)
130 0 : return;
131 0 : ipv4_input(ifp, m);
132 0 : return;
133 : #ifdef INET6
134 : case MPLS_LABEL_IPV6NULL:
135 : do_v6:
136 0 : if ((m = mpls_ip6_adjttl(m, ttl)) == NULL)
137 0 : return;
138 0 : ipv6_input(ifp, m);
139 0 : return;
140 : #endif /* INET6 */
141 : case MPLS_LABEL_IMPLNULL:
142 0 : if (m->m_len < sizeof(u_char) &&
143 0 : (m = m_pullup(m, sizeof(u_char))) == NULL)
144 0 : return;
145 0 : switch (*mtod(m, u_char *) >> 4) {
146 : case IPVERSION:
147 : goto do_v4;
148 : #ifdef INET6
149 : case IPV6_VERSION >> 4:
150 : goto do_v6;
151 : #endif
152 : default:
153 0 : m_freem(m);
154 0 : return;
155 : }
156 : default:
157 : /* Other cases are not handled for now */
158 0 : m_freem(m);
159 0 : return;
160 : }
161 : }
162 0 : }
163 :
164 : ifp = NULL;
165 :
166 0 : rt = rtalloc(smplstosa(smpls), RT_RESOLVE, m->m_pkthdr.ph_rtableid);
167 0 : if (rt == NULL) {
168 : /* no entry for this label */
169 : #ifdef MPLS_DEBUG
170 : printf("MPLS_DEBUG: label not found\n");
171 : #endif
172 0 : m_freem(m);
173 0 : return;
174 : }
175 :
176 0 : rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
177 0 : if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
178 : #ifdef MPLS_DEBUG
179 : printf("MPLS_DEBUG: no MPLS information attached\n");
180 : #endif
181 0 : m_freem(m);
182 0 : goto done;
183 : }
184 :
185 0 : switch (rt_mpls->mpls_operation) {
186 : case MPLS_OP_POP:
187 0 : m = mpls_shim_pop(m);
188 0 : if (m == NULL)
189 : goto done;
190 0 : if (!hasbos)
191 : /* just forward to gw */
192 : break;
193 :
194 : /* last label popped so decide where to push it to */
195 0 : ifp = if_get(rt->rt_ifidx);
196 0 : if (ifp == NULL) {
197 0 : m_freem(m);
198 0 : goto done;
199 : }
200 : #if NMPE > 0
201 0 : if (ifp->if_type == IFT_MPLS) {
202 0 : smpls = satosmpls(rt_key(rt));
203 0 : mpe_input(m, ifp, smpls, ttl);
204 0 : goto done;
205 : }
206 : #endif
207 0 : if (ifp->if_type == IFT_MPLSTUNNEL) {
208 0 : ifp->if_output(ifp, m, rt_key(rt), rt);
209 0 : goto done;
210 : }
211 :
212 0 : KASSERT(rt->rt_gateway);
213 :
214 0 : switch(rt->rt_gateway->sa_family) {
215 : case AF_INET:
216 0 : if ((m = mpls_ip_adjttl(m, ttl)) == NULL)
217 : goto done;
218 : break;
219 : #ifdef INET6
220 : case AF_INET6:
221 0 : if ((m = mpls_ip6_adjttl(m, ttl)) == NULL)
222 : goto done;
223 : break;
224 : #endif
225 : default:
226 0 : m_freem(m);
227 0 : goto done;
228 : }
229 :
230 : /* shortcut sending out the packet */
231 0 : if (!ISSET(ifp->if_xflags, IFXF_MPLS))
232 0 : (*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
233 : else
234 0 : (*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
235 : goto done;
236 : case MPLS_OP_PUSH:
237 : /* this does not make much sense but it does not hurt */
238 0 : m = mpls_shim_push(m, rt_mpls);
239 0 : break;
240 : case MPLS_OP_SWAP:
241 0 : m = mpls_shim_swap(m, rt_mpls);
242 0 : break;
243 : default:
244 0 : m_freem(m);
245 0 : goto done;
246 : }
247 :
248 0 : if (m == NULL)
249 : goto done;
250 :
251 : /* refetch label and write back TTL */
252 0 : shim = mtod(m, struct shim_hdr *);
253 0 : shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl);
254 :
255 0 : ifp = if_get(rt->rt_ifidx);
256 0 : if (ifp == NULL) {
257 0 : m_freem(m);
258 0 : goto done;
259 : }
260 : #ifdef MPLS_DEBUG
261 : printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n",
262 : ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family,
263 : MPLS_LABEL_GET(smpls->smpls_label),
264 : MPLS_LABEL_GET(rt_mpls->mpls_label));
265 : #endif
266 :
267 : /* Output iface is not MPLS-enabled */
268 0 : if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
269 : #ifdef MPLS_DEBUG
270 : printf("MPLS_DEBUG: interface %s not mpls enabled\n",
271 : ifp->if_xname);
272 : #endif
273 0 : m_freem(m);
274 0 : goto done;
275 : }
276 :
277 0 : (*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt);
278 : done:
279 0 : if_put(ifp);
280 0 : rtfree(rt);
281 0 : }
282 :
283 : struct mbuf *
284 0 : mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl)
285 : {
286 : struct ip *ip;
287 : int hlen;
288 :
289 0 : if (mpls_mapttl_ip) {
290 0 : if (m->m_len < sizeof(struct ip) &&
291 0 : (m = m_pullup(m, sizeof(struct ip))) == NULL)
292 0 : return NULL;
293 0 : ip = mtod(m, struct ip *);
294 0 : hlen = ip->ip_hl << 2;
295 0 : if (m->m_len < hlen) {
296 0 : if ((m = m_pullup(m, hlen)) == NULL)
297 0 : return NULL;
298 0 : ip = mtod(m, struct ip *);
299 0 : }
300 : /* make sure we have a valid header */
301 0 : if (in_cksum(m, hlen) != 0) {
302 0 : m_freem(m);
303 0 : return NULL;
304 : }
305 :
306 : /* set IP ttl from MPLS ttl */
307 0 : ip->ip_ttl = ttl;
308 :
309 : /* recalculate checksum */
310 0 : ip->ip_sum = 0;
311 0 : ip->ip_sum = in_cksum(m, hlen);
312 0 : }
313 0 : return m;
314 0 : }
315 :
316 : #ifdef INET6
317 : struct mbuf *
318 0 : mpls_ip6_adjttl(struct mbuf *m, u_int8_t ttl)
319 : {
320 : struct ip6_hdr *ip6hdr;
321 :
322 0 : if (mpls_mapttl_ip6) {
323 0 : if (m->m_len < sizeof(struct ip6_hdr) &&
324 0 : (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL)
325 0 : return NULL;
326 :
327 0 : ip6hdr = mtod(m, struct ip6_hdr *);
328 :
329 : /* set IPv6 ttl from MPLS ttl */
330 0 : ip6hdr->ip6_hlim = ttl;
331 0 : }
332 0 : return m;
333 0 : }
334 : #endif /* INET6 */
335 :
336 : struct mbuf *
337 0 : mpls_do_error(struct mbuf *m, int type, int code, int destmtu)
338 : {
339 0 : struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];
340 0 : struct sockaddr_mpls sa_mpls;
341 : struct sockaddr_mpls *smpls;
342 : struct rtentry *rt = NULL;
343 : struct shim_hdr *shim;
344 : struct in_ifaddr *ia;
345 : struct icmp *icp;
346 : struct ip *ip;
347 : int nstk, error;
348 :
349 0 : for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
350 0 : if (m->m_len < sizeof(*shim) &&
351 0 : (m = m_pullup(m, sizeof(*shim))) == NULL)
352 0 : return (NULL);
353 0 : stack[nstk] = *mtod(m, struct shim_hdr *);
354 0 : m_adj(m, sizeof(*shim));
355 0 : if (MPLS_BOS_ISSET(stack[nstk].shim_label))
356 : break;
357 : }
358 0 : shim = &stack[0];
359 :
360 0 : if (m->m_len < sizeof(u_char) &&
361 0 : (m = m_pullup(m, sizeof(u_char))) == NULL)
362 0 : return (NULL);
363 0 : switch (*mtod(m, u_char *) >> 4) {
364 : case IPVERSION:
365 0 : if (m->m_len < sizeof(*ip) &&
366 0 : (m = m_pullup(m, sizeof(*ip))) == NULL)
367 0 : return (NULL);
368 0 : m = icmp_do_error(m, type, code, 0, destmtu);
369 0 : if (m == NULL)
370 0 : return (NULL);
371 :
372 0 : if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,
373 0 : (nstk + 1) * sizeof(*shim)))
374 0 : return (NULL);
375 :
376 : /* set ip_src to something usable, based on the MPLS label */
377 0 : bzero(&sa_mpls, sizeof(sa_mpls));
378 : smpls = &sa_mpls;
379 0 : smpls->smpls_family = AF_MPLS;
380 0 : smpls->smpls_len = sizeof(*smpls);
381 0 : smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
382 :
383 0 : rt = rtalloc(smplstosa(smpls), RT_RESOLVE, 0);
384 0 : if (rt == NULL) {
385 : /* no entry for this label */
386 0 : m_freem(m);
387 0 : return (NULL);
388 : }
389 0 : if (rt->rt_ifa->ifa_addr->sa_family == AF_INET)
390 0 : ia = ifatoia(rt->rt_ifa);
391 : else {
392 : /* XXX this needs fixing, if the MPLS is on an IP
393 : * less interface we need to find some other IP to
394 : * use as source.
395 : */
396 0 : rtfree(rt);
397 0 : m_freem(m);
398 0 : return (NULL);
399 : }
400 : /* It is safe to dereference ``ia'' iff ``rt'' is valid. */
401 0 : error = icmp_reflect(m, NULL, ia);
402 0 : rtfree(rt);
403 0 : if (error)
404 0 : return (NULL);
405 :
406 0 : ip = mtod(m, struct ip *);
407 : /* stuff to fix up which is normaly done in ip_output */
408 0 : ip->ip_v = IPVERSION;
409 0 : ip->ip_id = htons(ip_randomid());
410 0 : ip->ip_sum = 0;
411 0 : ip->ip_sum = in_cksum(m, sizeof(*ip));
412 :
413 : /* stolen from icmp_send() */
414 0 : icp = (struct icmp *)(mtod(m, caddr_t) + sizeof(*ip));
415 0 : icp->icmp_cksum = 0;
416 0 : icp->icmp_cksum = in4_cksum(m, 0, sizeof(*ip),
417 0 : ntohs(ip->ip_len) - sizeof(*ip));
418 :
419 : break;
420 : #ifdef INET6
421 : case IPV6_VERSION >> 4:
422 : #endif
423 : default:
424 0 : m_freem(m);
425 0 : return (NULL);
426 : }
427 :
428 : /* add mpls stack back to new packet */
429 0 : M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);
430 0 : if (m == NULL)
431 0 : return (NULL);
432 0 : m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);
433 :
434 : /* change TTL to default */
435 0 : shim = mtod(m, struct shim_hdr *);
436 0 : shim->shim_label =
437 0 : (shim->shim_label & ~MPLS_TTL_MASK) | htonl(mpls_defttl);
438 :
439 0 : return (m);
440 0 : }
|