xref: /openbsd/sys/netmpls/mpls_input.c (revision 55055d61)
1 /*	$OpenBSD: mpls_input.c,v 1.79 2023/05/13 13:35:18 bluhm 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 <sys/param.h>
20 #include <sys/mbuf.h>
21 #include <sys/systm.h>
22 #include <sys/socket.h>
23 
24 #include <net/if.h>
25 #include <net/if_var.h>
26 #include <net/if_types.h>
27 #include <net/netisr.h>
28 #include <net/route.h>
29 
30 #include <netinet/in.h>
31 #include <netinet/ip.h>
32 #include <netinet/ip_var.h>
33 #include <netinet/ip_icmp.h>
34 
35 #ifdef INET6
36 #include <netinet/ip6.h>
37 #endif /* INET6 */
38 
39 #include <netmpls/mpls.h>
40 
41 #ifdef MPLS_DEBUG
42 #define MPLS_LABEL_GET(l)	((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
43 #define MPLS_TTL_GET(l)		(ntohl((l) & MPLS_TTL_MASK))
44 #endif
45 
46 struct mbuf	*mpls_do_error(struct mbuf *, int, int, int);
47 void		 mpls_input_local(struct rtentry *, struct mbuf *);
48 
49 void
mpls_input(struct ifnet * ifp,struct mbuf * m)50 mpls_input(struct ifnet *ifp, struct mbuf *m)
51 {
52 	struct sockaddr_mpls *smpls;
53 	struct sockaddr_mpls sa_mpls;
54 	struct shim_hdr	*shim;
55 	struct rtentry *rt;
56 	struct rt_mpls *rt_mpls;
57 	uint8_t ttl;
58 	int hasbos;
59 
60 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
61 		m_freem(m);
62 		return;
63 	}
64 
65 	/* drop all broadcast and multicast packets */
66 	if (m->m_flags & (M_BCAST | M_MCAST)) {
67 		m_freem(m);
68 		return;
69 	}
70 
71 	if (m->m_len < sizeof(*shim)) {
72 		m = m_pullup(m, sizeof(*shim));
73 		if (m == NULL)
74 			return;
75 	}
76 
77 	shim = mtod(m, struct shim_hdr *);
78 #ifdef MPLS_DEBUG
79 	printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n",
80 	    ifp->if_xname, MPLS_LABEL_GET(shim->shim_label),
81 	    MPLS_TTL_GET(shim->shim_label),
82 	    MPLS_BOS_ISSET(shim->shim_label));
83 #endif
84 
85 	/* check and decrement TTL */
86 	ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
87 	if (ttl <= 1) {
88 		/* TTL exceeded */
89 		m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0);
90 		if (m == NULL)
91 			return;
92 
93 		shim = mtod(m, struct shim_hdr *);
94 		ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
95 	} else
96 		ttl--;
97 	hasbos = MPLS_BOS_ISSET(shim->shim_label);
98 
99 	bzero(&sa_mpls, sizeof(sa_mpls));
100 	smpls = &sa_mpls;
101 	smpls->smpls_family = AF_MPLS;
102 	smpls->smpls_len = sizeof(*smpls);
103 	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
104 
105 	if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) {
106 		m = mpls_shim_pop(m);
107 		if (m == NULL)
108 			return;
109 		if (!hasbos) {
110 			/*
111 			 * RFC 4182 relaxes the position of the
112 			 * explicit NULL labels. They no longer need
113 			 * to be at the beginning of the stack.
114 			 * In this case the label is ignored and the decision
115 			 * is made based on the lower one.
116 			 */
117 			shim = mtod(m, struct shim_hdr *);
118 			smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
119 			hasbos = MPLS_BOS_ISSET(shim->shim_label);
120 		} else {
121 			switch (ntohl(smpls->smpls_label)) {
122 			case MPLS_LABEL_IPV4NULL:
123 do_v4:
124 				if (mpls_mapttl_ip) {
125 					m = mpls_ip_adjttl(m, ttl);
126 					if (m == NULL)
127 						return;
128 				}
129 				ipv4_input(ifp, m);
130 				return;
131 #ifdef INET6
132 			case MPLS_LABEL_IPV6NULL:
133 do_v6:
134 				if (mpls_mapttl_ip6) {
135 					m = mpls_ip6_adjttl(m, ttl);
136 					if (m == NULL)
137 						return;
138 				}
139 				ipv6_input(ifp, m);
140 				return;
141 #endif	/* INET6 */
142 			case MPLS_LABEL_IMPLNULL:
143 				if (m->m_len < sizeof(u_char) &&
144 				    (m = m_pullup(m, sizeof(u_char))) == NULL)
145 					return;
146 				switch (*mtod(m, u_char *) >> 4) {
147 				case IPVERSION:
148 					goto do_v4;
149 #ifdef INET6
150 				case IPV6_VERSION >> 4:
151 					goto do_v6;
152 #endif
153 				default:
154 					m_freem(m);
155 					return;
156 				}
157 			default:
158 				/* Other cases are not handled for now */
159 				m_freem(m);
160 				return;
161 			}
162 		}
163 	}
164 
165 	ifp = NULL;
166 
167 	rt = rtalloc(smplstosa(smpls), RT_RESOLVE, m->m_pkthdr.ph_rtableid);
168 	if (!rtisvalid(rt)) {
169 		/* no entry for this label */
170 #ifdef MPLS_DEBUG
171 		printf("MPLS_DEBUG: label not found\n");
172 #endif
173 		m_freem(m);
174 		goto done;
175 	}
176 
177 	rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
178 	if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
179 #ifdef MPLS_DEBUG
180 		printf("MPLS_DEBUG: no MPLS information attached\n");
181 #endif
182 		m_freem(m);
183 		goto done;
184 	}
185 
186 	switch (rt_mpls->mpls_operation) {
187 	case MPLS_OP_POP:
188 		if (ISSET(rt->rt_flags, RTF_LOCAL)) {
189 			mpls_input_local(rt, m);
190 			goto done;
191 		}
192 
193 		m = mpls_shim_pop(m);
194 		if (m == NULL)
195 			goto done;
196 		if (!hasbos)
197 			/* just forward to gw */
198 			break;
199 
200 		/* last label popped so decide where to push it to */
201 		ifp = if_get(rt->rt_ifidx);
202 		if (ifp == NULL) {
203 			m_freem(m);
204 			goto done;
205 		}
206 
207 		KASSERT(rt->rt_gateway);
208 
209 		switch(rt->rt_gateway->sa_family) {
210 		case AF_INET:
211 			if ((m = mpls_ip_adjttl(m, ttl)) == NULL)
212 				goto done;
213 			break;
214 #ifdef INET6
215 		case AF_INET6:
216 			if ((m = mpls_ip6_adjttl(m, ttl)) == NULL)
217 				goto done;
218 			break;
219 #endif
220 		case AF_LINK:
221 			break;
222 		default:
223 			m_freem(m);
224 			goto done;
225 		}
226 
227 		/* shortcut sending out the packet */
228 		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
229 			(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
230 		else
231 			(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
232 		goto done;
233 	case MPLS_OP_PUSH:
234 		/* this does not make much sense but it does not hurt */
235 		m = mpls_shim_push(m, rt_mpls);
236 		break;
237 	case MPLS_OP_SWAP:
238 		m = mpls_shim_swap(m, rt_mpls);
239 		break;
240 	default:
241 		m_freem(m);
242 		goto done;
243 	}
244 
245 	if (m == NULL)
246 		goto done;
247 
248 	/* refetch label and write back TTL */
249 	shim = mtod(m, struct shim_hdr *);
250 	shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl);
251 
252 	ifp = if_get(rt->rt_ifidx);
253 	if (ifp == NULL) {
254 		m_freem(m);
255 		goto done;
256 	}
257 #ifdef MPLS_DEBUG
258 	printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n",
259     	    ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family,
260 	    MPLS_LABEL_GET(smpls->smpls_label),
261 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
262 #endif
263 
264 	/* Output iface is not MPLS-enabled */
265 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
266 #ifdef MPLS_DEBUG
267 		printf("MPLS_DEBUG: interface %s not mpls enabled\n",
268 		    ifp->if_xname);
269 #endif
270 		m_freem(m);
271 		goto done;
272 	}
273 
274 	(*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt);
275 done:
276 	if_put(ifp);
277 	rtfree(rt);
278 }
279 
280 void
mpls_input_local(struct rtentry * rt,struct mbuf * m)281 mpls_input_local(struct rtentry *rt, struct mbuf *m)
282 {
283 	struct ifnet *ifp;
284 
285 	ifp = if_get(rt->rt_ifidx);
286 	if (ifp == NULL) {
287 		m_freem(m);
288 		return;
289 	}
290 
291 	/* shortcut sending out the packet */
292 	if (!ISSET(ifp->if_xflags, IFXF_MPLS))
293 		(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
294 	else
295 		(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
296 
297 	if_put(ifp);
298 }
299 
300 struct mbuf *
mpls_ip_adjttl(struct mbuf * m,u_int8_t ttl)301 mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl)
302 {
303 	struct ip *ip;
304 	uint16_t old, new;
305 	uint32_t x;
306 
307 	if (m->m_len < sizeof(*ip)) {
308 		m = m_pullup(m, sizeof(*ip));
309 		if (m == NULL)
310 			return (NULL);
311 	}
312 	ip = mtod(m, struct ip *);
313 
314 	old = htons(ip->ip_ttl << 8);
315 	new = htons(ttl << 8);
316 	x = ip->ip_sum + old - new;
317 
318 	ip->ip_ttl = ttl;
319 	/* see pf_cksum_fixup() */
320 	ip->ip_sum = (x) + (x >> 16);
321 
322 	return (m);
323 }
324 
325 #ifdef INET6
326 struct mbuf *
mpls_ip6_adjttl(struct mbuf * m,u_int8_t ttl)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 *
mpls_do_error(struct mbuf * m,int type,int code,int destmtu)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 (!rtisvalid(rt)) {
393 			rtfree(rt);
394 			/* no entry for this label */
395 			m_freem(m);
396 			return (NULL);
397 		}
398 		if (rt->rt_ifa->ifa_addr->sa_family == AF_INET)
399 			ia = ifatoia(rt->rt_ifa);
400 		else {
401 			/* XXX this needs fixing, if the MPLS is on an IP
402 			 * less interface we need to find some other IP to
403 			 * use as source.
404 			 */
405 			rtfree(rt);
406 			m_freem(m);
407 			return (NULL);
408 		}
409 		/* It is safe to dereference ``ia'' iff ``rt'' is valid. */
410 		error = icmp_reflect(m, NULL, ia);
411 		rtfree(rt);
412 		if (error)
413 			return (NULL);
414 
415 		ip = mtod(m, struct ip *);
416 		/* stuff to fix up which is normally done in ip_output */
417 		ip->ip_v = IPVERSION;
418 		ip->ip_id = htons(ip_randomid());
419 		in_hdr_cksum_out(m, NULL);
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