xref: /openbsd-src/sys/netmpls/mpls_input.c (revision 5e3c7963eb248119b7dfd4b0defad58a7d9cd306)
1 /*	$OpenBSD: mpls_input.c,v 1.73 2019/01/27 05:13:04 dlg 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_do_error(struct mbuf *, int, int, int);
49 void		 mpls_input_local(struct rtentry *, struct mbuf *);
50 
51 void
52 mpls_input(struct ifnet *ifp, struct mbuf *m)
53 {
54 	struct sockaddr_mpls *smpls;
55 	struct sockaddr_mpls sa_mpls;
56 	struct shim_hdr	*shim;
57 	struct rtentry *rt;
58 	struct rt_mpls *rt_mpls;
59 	uint8_t ttl;
60 	int hasbos;
61 
62 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
63 		m_freem(m);
64 		return;
65 	}
66 
67 	/* drop all broadcast and multicast packets */
68 	if (m->m_flags & (M_BCAST | M_MCAST)) {
69 		m_freem(m);
70 		return;
71 	}
72 
73 	if (m->m_len < sizeof(*shim)) {
74 		m = m_pullup(m, sizeof(*shim));
75 		if (m == NULL)
76 			return;
77 	}
78 
79 	shim = mtod(m, struct shim_hdr *);
80 #ifdef MPLS_DEBUG
81 	printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n",
82 	    ifp->if_xname, MPLS_LABEL_GET(shim->shim_label),
83 	    MPLS_TTL_GET(shim->shim_label),
84 	    MPLS_BOS_ISSET(shim->shim_label));
85 #endif
86 
87 	/* check and decrement TTL */
88 	ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
89 	if (ttl <= 1) {
90 		/* TTL exceeded */
91 		m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0);
92 		if (m == NULL)
93 			return;
94 
95 		shim = mtod(m, struct shim_hdr *);
96 		ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
97 	} else
98 		ttl--;
99 	hasbos = MPLS_BOS_ISSET(shim->shim_label);
100 
101 	bzero(&sa_mpls, sizeof(sa_mpls));
102 	smpls = &sa_mpls;
103 	smpls->smpls_family = AF_MPLS;
104 	smpls->smpls_len = sizeof(*smpls);
105 	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
106 
107 	if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) {
108 		m = mpls_shim_pop(m);
109 		if (m == NULL)
110 			return;
111 		if (!hasbos) {
112 			/*
113 			 * RFC 4182 relaxes the position of the
114 			 * explicit NULL labels. They no longer need
115 			 * to be at the beginning of the stack.
116 			 * In this case the label is ignored and the decision
117 			 * is made based on the lower one.
118 			 */
119 			shim = mtod(m, struct shim_hdr *);
120 			smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
121 			hasbos = MPLS_BOS_ISSET(shim->shim_label);
122 		} else {
123 			switch (ntohl(smpls->smpls_label)) {
124 			case MPLS_LABEL_IPV4NULL:
125 do_v4:
126 				if (mpls_mapttl_ip) {
127 					m = mpls_ip_adjttl(m, ttl);
128 					if (m == NULL)
129 						return;
130 				}
131 				ipv4_input(ifp, m);
132 				return;
133 #ifdef INET6
134 			case MPLS_LABEL_IPV6NULL:
135 do_v6:
136 				if (mpls_mapttl_ip6) {
137 					m = mpls_ip6_adjttl(m, ttl);
138 					if (m == NULL)
139 						return;
140 				}
141 				ipv6_input(ifp, m);
142 				return;
143 #endif	/* INET6 */
144 			case MPLS_LABEL_IMPLNULL:
145 				if (m->m_len < sizeof(u_char) &&
146 				    (m = m_pullup(m, sizeof(u_char))) == NULL)
147 					return;
148 				switch (*mtod(m, u_char *) >> 4) {
149 				case IPVERSION:
150 					goto do_v4;
151 #ifdef INET6
152 				case IPV6_VERSION >> 4:
153 					goto do_v6;
154 #endif
155 				default:
156 					m_freem(m);
157 					return;
158 				}
159 			default:
160 				/* Other cases are not handled for now */
161 				m_freem(m);
162 				return;
163 			}
164 		}
165 	}
166 
167 	ifp = NULL;
168 
169 	rt = rtalloc(smplstosa(smpls), RT_RESOLVE, m->m_pkthdr.ph_rtableid);
170 	if (rt == NULL) {
171 		/* no entry for this label */
172 #ifdef MPLS_DEBUG
173 		printf("MPLS_DEBUG: label not found\n");
174 #endif
175 		m_freem(m);
176 		return;
177 	}
178 
179 	rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
180 	if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
181 #ifdef MPLS_DEBUG
182 		printf("MPLS_DEBUG: no MPLS information attached\n");
183 #endif
184 		m_freem(m);
185 		goto done;
186 	}
187 
188 	switch (rt_mpls->mpls_operation) {
189 	case MPLS_OP_POP:
190 		if (ISSET(rt->rt_flags, RTF_LOCAL)) {
191 			mpls_input_local(rt, m);
192 			goto done;
193 		}
194 
195 		m = mpls_shim_pop(m);
196 		if (m == NULL)
197 			goto done;
198 		if (!hasbos)
199 			/* just forward to gw */
200 			break;
201 
202 		/* last label popped so decide where to push it to */
203 		ifp = if_get(rt->rt_ifidx);
204 		if (ifp == NULL) {
205 			m_freem(m);
206 			goto done;
207 		}
208 
209 		KASSERT(rt->rt_gateway);
210 
211 		switch(rt->rt_gateway->sa_family) {
212 		case AF_INET:
213 			if ((m = mpls_ip_adjttl(m, ttl)) == NULL)
214 				goto done;
215 			break;
216 #ifdef INET6
217 		case AF_INET6:
218 			if ((m = mpls_ip6_adjttl(m, ttl)) == NULL)
219 				goto done;
220 			break;
221 #endif
222 		case AF_LINK:
223 			break;
224 		default:
225 			m_freem(m);
226 			goto done;
227 		}
228 
229 		/* shortcut sending out the packet */
230 		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
231 			(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
232 		else
233 			(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
234 		goto done;
235 	case MPLS_OP_PUSH:
236 		/* this does not make much sense but it does not hurt */
237 		m = mpls_shim_push(m, rt_mpls);
238 		break;
239 	case MPLS_OP_SWAP:
240 		m = mpls_shim_swap(m, rt_mpls);
241 		break;
242 	default:
243 		m_freem(m);
244 		goto done;
245 	}
246 
247 	if (m == NULL)
248 		goto done;
249 
250 	/* refetch label and write back TTL */
251 	shim = mtod(m, struct shim_hdr *);
252 	shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl);
253 
254 	ifp = if_get(rt->rt_ifidx);
255 	if (ifp == NULL) {
256 		m_freem(m);
257 		goto done;
258 	}
259 #ifdef MPLS_DEBUG
260 	printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n",
261     	    ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family,
262 	    MPLS_LABEL_GET(smpls->smpls_label),
263 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
264 #endif
265 
266 	/* Output iface is not MPLS-enabled */
267 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
268 #ifdef MPLS_DEBUG
269 		printf("MPLS_DEBUG: interface %s not mpls enabled\n",
270 		    ifp->if_xname);
271 #endif
272 		m_freem(m);
273 		goto done;
274 	}
275 
276 	(*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt);
277 done:
278 	if_put(ifp);
279 	rtfree(rt);
280 }
281 
282 void
283 mpls_input_local(struct rtentry *rt, struct mbuf *m)
284 {
285 	struct ifnet *ifp;
286 
287 	ifp = if_get(rt->rt_ifidx);
288 	if (ifp == NULL) {
289 		m_freem(m);
290 		return;
291 	}
292 
293 	/* shortcut sending out the packet */
294 	if (!ISSET(ifp->if_xflags, IFXF_MPLS))
295 		(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
296 	else
297 		(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
298 
299 	if_put(ifp);
300 }
301 
302 struct mbuf *
303 mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl)
304 {
305 	struct ip *ip;
306 	uint16_t old, new, x;
307 
308 	if (m->m_len < sizeof(*ip)) {
309 		m = m_pullup(m, sizeof(*ip));
310 		if (m == NULL)
311 			return (NULL);
312 	}
313 	ip = mtod(m, struct ip *);
314 
315 	old = htons(ip->ip_ttl << 8);
316 	new = htons(ttl << 8);
317 	x = ip->ip_sum + old - new;
318 
319 	ip->ip_ttl = ttl;
320 	ip->ip_sum = (x) + (x >> 16);
321 
322 	return (m);
323 }
324 
325 #ifdef INET6
326 struct mbuf *
327 mpls_ip6_adjttl(struct mbuf *m, u_int8_t ttl)
328 {
329 	struct ip6_hdr *ip6;
330 
331 	if (m->m_len < sizeof(*ip6)) {
332 		m = m_pullup(m, sizeof(*ip6));
333 		if (m == NULL)
334 			return (NULL);
335 	}
336 	ip6 = mtod(m, struct ip6_hdr *);
337 
338 	ip6->ip6_hlim = ttl;
339 
340 	return (m);
341 }
342 #endif	/* INET6 */
343 
344 struct mbuf *
345 mpls_do_error(struct mbuf *m, int type, int code, int destmtu)
346 {
347 	struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];
348 	struct sockaddr_mpls sa_mpls;
349 	struct sockaddr_mpls *smpls;
350 	struct rtentry *rt = NULL;
351 	struct shim_hdr *shim;
352 	struct in_ifaddr *ia;
353 	struct icmp *icp;
354 	struct ip *ip;
355 	int nstk, error;
356 
357 	for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
358 		if (m->m_len < sizeof(*shim) &&
359 		    (m = m_pullup(m, sizeof(*shim))) == NULL)
360 			return (NULL);
361 		stack[nstk] = *mtod(m, struct shim_hdr *);
362 		m_adj(m, sizeof(*shim));
363 		if (MPLS_BOS_ISSET(stack[nstk].shim_label))
364 			break;
365 	}
366 	shim = &stack[0];
367 
368 	if (m->m_len < sizeof(u_char) &&
369 	    (m = m_pullup(m, sizeof(u_char))) == NULL)
370 		return (NULL);
371 	switch (*mtod(m, u_char *) >> 4) {
372 	case IPVERSION:
373 		if (m->m_len < sizeof(*ip) &&
374 		    (m = m_pullup(m, sizeof(*ip))) == NULL)
375 			return (NULL);
376 		m = icmp_do_error(m, type, code, 0, destmtu);
377 		if (m == NULL)
378 			return (NULL);
379 
380 		if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,
381 		    (nstk + 1) * sizeof(*shim)))
382 			return (NULL);
383 
384 		/* set ip_src to something usable, based on the MPLS label */
385 		bzero(&sa_mpls, sizeof(sa_mpls));
386 		smpls = &sa_mpls;
387 		smpls->smpls_family = AF_MPLS;
388 		smpls->smpls_len = sizeof(*smpls);
389 		smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
390 
391 		rt = rtalloc(smplstosa(smpls), RT_RESOLVE, 0);
392 		if (rt == NULL) {
393 			/* no entry for this label */
394 			m_freem(m);
395 			return (NULL);
396 		}
397 		if (rt->rt_ifa->ifa_addr->sa_family == AF_INET)
398 			ia = ifatoia(rt->rt_ifa);
399 		else {
400 			/* XXX this needs fixing, if the MPLS is on an IP
401 			 * less interface we need to find some other IP to
402 			 * use as source.
403 			 */
404 			rtfree(rt);
405 			m_freem(m);
406 			return (NULL);
407 		}
408 		/* It is safe to dereference ``ia'' iff ``rt'' is valid. */
409 		error = icmp_reflect(m, NULL, ia);
410 		rtfree(rt);
411 		if (error)
412 			return (NULL);
413 
414 		ip = mtod(m, struct ip *);
415 		/* stuff to fix up which is normaly done in ip_output */
416 		ip->ip_v = IPVERSION;
417 		ip->ip_id = htons(ip_randomid());
418 		ip->ip_sum = 0;
419 		ip->ip_sum = in_cksum(m, sizeof(*ip));
420 
421 		/* stolen from icmp_send() */
422 		icp = (struct icmp *)(mtod(m, caddr_t) + sizeof(*ip));
423 		icp->icmp_cksum = 0;
424 		icp->icmp_cksum = in4_cksum(m, 0, sizeof(*ip),
425 		    ntohs(ip->ip_len) - sizeof(*ip));
426 
427 		break;
428 #ifdef INET6
429 	case IPV6_VERSION >> 4:
430 #endif
431 	default:
432 		m_freem(m);
433 		return (NULL);
434 	}
435 
436 	/* add mpls stack back to new packet */
437 	M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);
438 	if (m == NULL)
439 		return (NULL);
440 	m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);
441 
442 	/* change TTL to default */
443 	shim = mtod(m, struct shim_hdr *);
444 	shim->shim_label =
445 	    (shim->shim_label & ~MPLS_TTL_MASK) | htonl(mpls_defttl);
446 
447 	return (m);
448 }
449