xref: /openbsd/sys/netmpls/mpls_input.c (revision cca36db2)
1 /*	$OpenBSD: mpls_input.c,v 1.32 2011/07/06 02:42:28 henning 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_types.h>
28 #include <net/netisr.h>
29 #include <net/route.h>
30 
31 #ifdef  INET
32 #include <netinet/in.h>
33 #include <netinet/in_var.h>
34 #include <netinet/in_systm.h>
35 #include <netinet/ip.h>
36 #include <netinet/ip_var.h>
37 #include <netinet/ip_icmp.h>
38 #endif
39 
40 #ifdef INET6
41 #include <netinet/ip6.h>
42 #ifndef INET
43 #include <netinet/in.h>
44 #endif
45 #endif /* INET6 */
46 
47 #include <netmpls/mpls.h>
48 
49 struct ifqueue	mplsintrq;
50 int		mplsqmaxlen = IFQ_MAXLEN;
51 extern int	mpls_inkloop;
52 
53 #ifdef MPLS_DEBUG
54 #define MPLS_LABEL_GET(l)	((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
55 #define MPLS_TTL_GET(l)		(ntohl((l) & MPLS_TTL_MASK))
56 #endif
57 
58 int	mpls_ip_adjttl(struct mbuf *, u_int8_t);
59 #ifdef INET6
60 int	mpls_ip6_adjttl(struct mbuf *, u_int8_t);
61 #endif
62 
63 struct mbuf	*mpls_do_error(struct mbuf *, int, int, int);
64 
65 void
66 mpls_init(void)
67 {
68 	IFQ_SET_MAXLEN(&mplsintrq, mplsqmaxlen);
69 }
70 
71 void
72 mplsintr(void)
73 {
74 	struct mbuf *m;
75 	int s;
76 
77 	for (;;) {
78 		/* Get next datagram of input queue */
79 		s = splnet();
80 		IF_DEQUEUE(&mplsintrq, m);
81 		splx(s);
82 		if (m == NULL)
83 			return;
84 #ifdef DIAGNOSTIC
85 		if ((m->m_flags & M_PKTHDR) == 0)
86 			panic("mplsintr no HDR");
87 #endif
88 		mpls_input(m);
89 	}
90 }
91 
92 void
93 mpls_input(struct mbuf *m)
94 {
95 	struct ifnet *ifp = m->m_pkthdr.rcvif;
96 	struct sockaddr_mpls *smpls;
97 	struct sockaddr_mpls sa_mpls;
98 	struct shim_hdr	*shim;
99 	struct rtentry *rt = NULL;
100 	struct rt_mpls *rt_mpls;
101 	u_int8_t ttl;
102 	int i, s, hasbos;
103 
104 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
105 		m_freem(m);
106 		return;
107 	}
108 
109 	/* drop all broadcast and multicast packets */
110 	if (m->m_flags & (M_BCAST | M_MCAST)) {
111 		m_freem(m);
112 		return;
113 	}
114 
115 	if (m->m_len < sizeof(*shim))
116 		if ((m = m_pullup(m, sizeof(*shim))) == NULL)
117 			return;
118 
119 	shim = mtod(m, struct shim_hdr *);
120 
121 #ifdef MPLS_DEBUG
122 	printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n",
123 	    ifp->if_xname, MPLS_LABEL_GET(shim->shim_label),
124 	    MPLS_TTL_GET(shim->shim_label),
125 	    MPLS_BOS_ISSET(shim->shim_label));
126 #endif
127 
128 	/* check and decrement TTL */
129 	ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
130 	if (ttl-- <= 1) {
131 		/* TTL exceeded */
132 		m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0);
133 		if (m == NULL)
134 			return;
135 		shim = mtod(m, struct shim_hdr *);
136 		ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
137 	}
138 
139 	bzero(&sa_mpls, sizeof(sa_mpls));
140 	smpls = &sa_mpls;
141 	smpls->smpls_family = AF_MPLS;
142 	smpls->smpls_len = sizeof(*smpls);
143 	for (i = 0; i < mpls_inkloop; i++) {
144 		smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
145 
146 #ifdef MPLS_DEBUG
147 		printf("smpls af %d len %d in_label %d in_ifindex %d\n",
148 		    smpls->smpls_family, smpls->smpls_len,
149 		    MPLS_LABEL_GET(smpls->smpls_label),
150 		    ifp->if_index);
151 #endif
152 
153 		if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) {
154 			hasbos = MPLS_BOS_ISSET(shim->shim_label);
155 			m = mpls_shim_pop(m);
156 			shim = mtod(m, struct shim_hdr *);
157 
158 			switch (ntohl(smpls->smpls_label)) {
159 			case MPLS_LABEL_IPV4NULL:
160 				/*
161 				 * RFC 4182 relaxes the position of the
162 				 * explicit NULL labels. The no longer need
163 				 * to be at the beginning of the stack.
164 				 */
165 				if (hasbos) {
166 do_v4:
167 					if (mpls_ip_adjttl(m, ttl))
168 						goto done;
169 					s = splnet();
170 					IF_INPUT_ENQUEUE(&ipintrq, m);
171 					schednetisr(NETISR_IP);
172 					splx(s);
173 					goto done;
174 				}
175 				continue;
176 #ifdef INET6
177 			case MPLS_LABEL_IPV6NULL:
178 				if (hasbos) {
179 do_v6:
180 					if (mpls_ip6_adjttl(m, ttl))
181 						goto done;
182 					s = splnet();
183 					IF_INPUT_ENQUEUE(&ip6intrq, m);
184 					schednetisr(NETISR_IPV6);
185 					splx(s);
186 					goto done;
187 				}
188 				continue;
189 #endif	/* INET6 */
190 			case MPLS_LABEL_IMPLNULL:
191 				if (hasbos) {
192 					switch (*mtod(m, u_char *) >> 4) {
193 					case IPVERSION:
194 						goto do_v4;
195 #ifdef INET6
196 					case IPV6_VERSION >> 4:
197 						goto do_v6;
198 #endif
199 					default:
200 						m_freem(m);
201 						goto done;
202 					}
203 				}
204 				/* FALLTHROUGH */
205 			default:
206 				/* Other cases are not handled for now */
207 				m_freem(m);
208 				goto done;
209 			}
210 		}
211 
212 		rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0);
213 		if (rt == NULL) {
214 			/* no entry for this label */
215 #ifdef MPLS_DEBUG
216 			printf("MPLS_DEBUG: label not found\n");
217 #endif
218 			m_freem(m);
219 			goto done;
220 		}
221 
222 		rt->rt_use++;
223 		rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
224 
225 		if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
226 #ifdef MPLS_DEBUG
227 			printf("MPLS_DEBUG: no MPLS information "
228 			    "attached\n");
229 #endif
230 			m_freem(m);
231 			goto done;
232 		}
233 
234 		hasbos = MPLS_BOS_ISSET(shim->shim_label);
235 		switch (rt_mpls->mpls_operation) {
236 		case MPLS_OP_LOCAL:
237 			/* Packet is for us */
238 			m = mpls_shim_pop(m);
239 			if (!hasbos)
240 				/* redo lookup with next label */
241 				break;
242 
243 			if (!rt->rt_gateway) {
244 				m_freem(m);
245 				goto done;
246 			}
247 
248 			switch(rt->rt_gateway->sa_family) {
249 			case AF_INET:
250 				if (mpls_ip_adjttl(m, ttl))
251 					break;
252 				s = splnet();
253 				IF_INPUT_ENQUEUE(&ipintrq, m);
254 				schednetisr(NETISR_IP);
255 				splx(s);
256 				break;
257 #ifdef INET6
258 			case AF_INET6:
259 				if (mpls_ip6_adjttl(m, ttl))
260 					break;
261 				s = splnet();
262 				IF_INPUT_ENQUEUE(&ip6intrq, m);
263 				schednetisr(NETISR_IPV6);
264 				splx(s);
265 				break;
266 #endif
267 			default:
268 				m_freem(m);
269 			}
270 			goto done;
271 		case MPLS_OP_POP:
272 			m = mpls_shim_pop(m);
273 			if (!hasbos)
274 				/* redo lookup with next label */
275 				break;
276 
277 			ifp = rt->rt_ifp;
278 #if NMPE > 0
279 			if (ifp->if_type == IFT_MPLS) {
280 				smpls = satosmpls(rt_key(rt));
281 				mpe_input(m, rt->rt_ifp, smpls, ttl);
282 				goto done;
283 			}
284 #endif
285 			if (!rt->rt_gateway) {
286 				m_freem(m);
287 				goto done;
288 			}
289 
290 			switch(rt->rt_gateway->sa_family) {
291 			case AF_INET:
292 				if (mpls_ip_adjttl(m, ttl))
293 					goto done;
294 				break;
295 #ifdef INET6
296 			case AF_INET6:
297 				if (mpls_ip6_adjttl(m, ttl))
298 					goto done;
299 				break;
300 #endif
301 			default:
302 				m_freem(m);
303 				goto done;
304 			}
305 
306 			/* Output iface is not MPLS-enabled */
307 			if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
308 				m_freem(m);
309 				goto done;
310 			}
311 
312 			(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
313 			goto done;
314 		case MPLS_OP_PUSH:
315 			m = mpls_shim_push(m, rt_mpls);
316 			break;
317 		case MPLS_OP_SWAP:
318 			m = mpls_shim_swap(m, rt_mpls);
319 			break;
320 		}
321 
322 		if (m == NULL)
323 			goto done;
324 
325 		/* refetch label */
326 		shim = mtod(m, struct shim_hdr *);
327 
328 		ifp = rt->rt_ifp;
329 		if (ifp != NULL && rt_mpls->mpls_operation != MPLS_OP_LOCAL)
330 			break;
331 
332 		RTFREE(rt);
333 		rt = NULL;
334 	}
335 
336 	if (rt == NULL) {
337 		m_freem(m);
338 		goto done;
339 	}
340 
341 	/* write back TTL */
342 	shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl);
343 
344 #ifdef MPLS_DEBUG
345 	printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n",
346     	    ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family,
347 	    MPLS_LABEL_GET(smpls->smpls_label),
348 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
349 #endif
350 
351 	/* Output iface is not MPLS-enabled */
352 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
353 #ifdef MPLS_DEBUG
354 		printf("MPLS_DEBUG: interface not mpls enabled\n");
355 #endif
356 		goto done;
357 	}
358 
359 	(*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt);
360 done:
361 	if (rt)
362 		RTFREE(rt);
363 }
364 
365 int
366 mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl)
367 {
368 	struct ip *ip;
369 	int hlen;
370 
371 	if (mpls_mapttl_ip) {
372 		if (m->m_len < sizeof(struct ip) &&
373 		    (m = m_pullup(m, sizeof(struct ip))) == NULL)
374 			return -1;
375 		ip = mtod(m, struct ip *);
376 		hlen = ip->ip_hl << 2;
377 		if (m->m_len < hlen) {
378 			if ((m = m_pullup(m, hlen)) == NULL)
379 				return -1;
380 			ip = mtod(m, struct ip *);
381 		}
382 		/* make sure we have a valid header */
383 		if (in_cksum(m, hlen) != 0) {
384 			m_free(m);
385 			return -1;
386 		}
387 
388 		/* set IP ttl from MPLS ttl */
389 		ip->ip_ttl = ttl;
390 
391 		/* recalculate checksum */
392 		ip->ip_sum = 0;
393 		ip->ip_sum = in_cksum(m, hlen);
394 	}
395 	return 0;
396 }
397 
398 #ifdef INET6
399 int
400 mpls_ip6_adjttl(struct mbuf *m, u_int8_t ttl)
401 {
402 	struct ip6_hdr *ip6hdr;
403 
404 	if (mpls_mapttl_ip6) {
405 		if (m->m_len < sizeof(struct ip6_hdr) &&
406 		    (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL)
407 			return -1;
408 
409 		ip6hdr = mtod(m, struct ip6_hdr *);
410 
411 		/* set IPv6 ttl from MPLS ttl */
412 		ip6hdr->ip6_hlim = ttl;
413 	}
414 	return 0;
415 }
416 #endif	/* INET6 */
417 
418 struct mbuf *
419 mpls_do_error(struct mbuf *m, int type, int code, int destmtu)
420 {
421 	struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];
422 	struct sockaddr_mpls sa_mpls;
423 	struct sockaddr_mpls *smpls;
424 	struct rtentry *rt = NULL;
425 	struct shim_hdr *shim;
426 	struct in_ifaddr *ia;
427 	struct icmp *icp;
428 	struct ip *ip;
429 	int nstk;
430 
431 	for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
432 		if (m->m_len < sizeof(*shim) &&
433 		    (m = m_pullup(m, sizeof(*ip))) == NULL)
434 			return (NULL);
435 		stack[nstk] = *mtod(m, struct shim_hdr *);
436 		m_adj(m, sizeof(*shim));
437 		if (MPLS_BOS_ISSET(stack[nstk].shim_label))
438 			break;
439 	}
440 	shim = &stack[0];
441 
442 	switch (*mtod(m, u_char *) >> 4) {
443 	case IPVERSION:
444 		if (m->m_len < sizeof(*ip) &&
445 		    (m = m_pullup(m, sizeof(*ip))) == NULL)
446 			return (NULL);
447 		m = icmp_do_error(m, type, code, 0, destmtu);
448 		if (m == NULL)
449 			return (NULL);
450 
451 		if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,
452 		    (nstk + 1) * sizeof(*shim)))
453 			return (NULL);
454 
455 		/* set ip_src to something usable, based on the MPLS label */
456 		bzero(&sa_mpls, sizeof(sa_mpls));
457 		smpls = &sa_mpls;
458 		smpls->smpls_family = AF_MPLS;
459 		smpls->smpls_len = sizeof(*smpls);
460 		smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
461 
462 		rt = rtalloc1(smplstosa(smpls), RT_REPORT, 0);
463 		if (rt == NULL) {
464 			/* no entry for this label */
465 			m_freem(m);
466 			return (NULL);
467 		}
468 		if (rt->rt_ifa->ifa_addr->sa_family == AF_INET)
469 			ia = ifatoia(rt->rt_ifa);
470 		else {
471 			/* XXX this needs fixing, if the MPLS is on an IP
472 			 * less interface we need to find some other IP to
473 			 * use as source.
474 			 */
475 			RTFREE(rt);
476 			m_freem(m);
477 			return (NULL);
478 		}
479 		rt->rt_use++;
480 		RTFREE(rt);
481 		if (icmp_reflect(m, NULL, ia))
482 			return (NULL);
483 
484 		ip = mtod(m, struct ip *);
485 		/* stuff to fix up which is normaly done in ip_output */
486 		ip->ip_v = IPVERSION;
487 		ip->ip_id = htons(ip_randomid());
488 		ip->ip_sum = 0;
489 		ip->ip_sum = in_cksum(m, sizeof(*ip));
490 
491 		/* stolen from icmp_send() */
492 		m->m_data += sizeof(*ip);
493 		m->m_len -= sizeof(*ip);
494 		icp = mtod(m, struct icmp *);
495 		icp->icmp_cksum = 0;
496 		icp->icmp_cksum = in_cksum(m, ntohs(ip->ip_len) - sizeof(*ip));
497 		m->m_data -= sizeof(*ip);
498 		m->m_len += sizeof(*ip);
499 
500 		break;
501 #ifdef INET6
502 	case IPV6_VERSION >> 4:
503 #endif
504 	default:
505 		m_freem(m);
506 		return (NULL);
507 	}
508 
509 	/* add mpls stack back to new packet */
510 	M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);
511 	if (m == NULL)
512 		return (NULL);
513 	m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);
514 
515 	/* change TTL to default */
516 	shim = mtod(m, struct shim_hdr *);
517 	shim->shim_label =
518 	    (shim->shim_label & ~MPLS_TTL_MASK) | htonl(mpls_defttl);
519 
520 	return (m);
521 }
522