xref: /openbsd-src/sys/netmpls/mpls_input.c (revision 55055d619d36cc45f8c6891404c51cd405214e86)
1*55055d61Sbluhm /*	$OpenBSD: mpls_input.c,v 1.79 2023/05/13 13:35:18 bluhm Exp $	*/
2d90b9793Snorby 
3d90b9793Snorby /*
4d90b9793Snorby  * Copyright (c) 2008 Claudio Jeker <claudio@openbsd.org>
5d90b9793Snorby  *
6d90b9793Snorby  * Permission to use, copy, modify, and distribute this software for any
7d90b9793Snorby  * purpose with or without fee is hereby granted, provided that the above
8d90b9793Snorby  * copyright notice and this permission notice appear in all copies.
9d90b9793Snorby  *
10d90b9793Snorby  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d90b9793Snorby  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d90b9793Snorby  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d90b9793Snorby  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d90b9793Snorby  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d90b9793Snorby  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d90b9793Snorby  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d90b9793Snorby  */
18d90b9793Snorby 
19d90b9793Snorby #include <sys/param.h>
20d90b9793Snorby #include <sys/mbuf.h>
21d90b9793Snorby #include <sys/systm.h>
22d90b9793Snorby #include <sys/socket.h>
23d90b9793Snorby 
24d90b9793Snorby #include <net/if.h>
250deb6685Smpi #include <net/if_var.h>
263b0a6083Sclaudio #include <net/if_types.h>
27dedfd255Smichele #include <net/netisr.h>
28d90b9793Snorby #include <net/route.h>
29d90b9793Snorby 
30dedfd255Smichele #include <netinet/in.h>
31dedfd255Smichele #include <netinet/ip.h>
32201d6983Sclaudio #include <netinet/ip_var.h>
33201d6983Sclaudio #include <netinet/ip_icmp.h>
34dedfd255Smichele 
35dedfd255Smichele #ifdef INET6
36dedfd255Smichele #include <netinet/ip6.h>
37dedfd255Smichele #endif /* INET6 */
38dedfd255Smichele 
39d90b9793Snorby #include <netmpls/mpls.h>
40d90b9793Snorby 
41d90b9793Snorby #ifdef MPLS_DEBUG
42d90b9793Snorby #define MPLS_LABEL_GET(l)	((ntohl((l) & MPLS_LABEL_MASK)) >> MPLS_LABEL_OFFSET)
43d90b9793Snorby #define MPLS_TTL_GET(l)		(ntohl((l) & MPLS_TTL_MASK))
44d90b9793Snorby #endif
45d90b9793Snorby 
46201d6983Sclaudio struct mbuf	*mpls_do_error(struct mbuf *, int, int, int);
4722b44b88Sdlg void		 mpls_input_local(struct rtentry *, struct mbuf *);
48201d6983Sclaudio 
49d90b9793Snorby void
mpls_input(struct ifnet * ifp,struct mbuf * m)505c53b932Sdlg mpls_input(struct ifnet *ifp, struct mbuf *m)
5159c24256Srzalamena {
52d90b9793Snorby 	struct sockaddr_mpls *smpls;
53cd776ce1Smichele 	struct sockaddr_mpls sa_mpls;
54d90b9793Snorby 	struct shim_hdr	*shim;
555b5e4895Sclaudio 	struct rtentry *rt;
56cd776ce1Smichele 	struct rt_mpls *rt_mpls;
575c53b932Sdlg 	uint8_t ttl;
585b5e4895Sclaudio 	int hasbos;
590bdd9363Snorby 
6002a95700Sjca 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
6102a95700Sjca 		m_freem(m);
6202a95700Sjca 		return;
6302a95700Sjca 	}
6402a95700Sjca 
658aff85b2Snorby 	/* drop all broadcast and multicast packets */
668aff85b2Snorby 	if (m->m_flags & (M_BCAST | M_MCAST)) {
678aff85b2Snorby 		m_freem(m);
68da955d8eSrzalamena 		return;
698aff85b2Snorby 	}
708aff85b2Snorby 
715c53b932Sdlg 	if (m->m_len < sizeof(*shim)) {
725c53b932Sdlg 		m = m_pullup(m, sizeof(*shim));
735c53b932Sdlg 		if (m == NULL)
74da955d8eSrzalamena 			return;
75834dde95Sderaadt 	}
76d90b9793Snorby 
77d90b9793Snorby 	shim = mtod(m, struct shim_hdr *);
78d90b9793Snorby #ifdef MPLS_DEBUG
795c53b932Sdlg 	printf("mpls_input: iface %s label=%d, ttl=%d BoS %d\n",
804da7852eSdlg 	    ifp->if_xname, MPLS_LABEL_GET(shim->shim_label),
8102a95700Sjca 	    MPLS_TTL_GET(shim->shim_label),
824da7852eSdlg 	    MPLS_BOS_ISSET(shim->shim_label));
83fd5d2b88Sclaudio #endif
84d90b9793Snorby 
85d90b9793Snorby 	/* check and decrement TTL */
864da7852eSdlg 	ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
87011c12f1Sdlg 	if (ttl <= 1) {
88d90b9793Snorby 		/* TTL exceeded */
89201d6983Sclaudio 		m = mpls_do_error(m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0);
905c53b932Sdlg 		if (m == NULL)
91da955d8eSrzalamena 			return;
925c53b932Sdlg 
93201d6983Sclaudio 		shim = mtod(m, struct shim_hdr *);
94201d6983Sclaudio 		ttl = ntohl(shim->shim_label & MPLS_TTL_MASK);
95011c12f1Sdlg 	} else
96011c12f1Sdlg 		ttl--;
975c53b932Sdlg 	hasbos = MPLS_BOS_ISSET(shim->shim_label);
98d90b9793Snorby 
99bd9d619aSnorby 	bzero(&sa_mpls, sizeof(sa_mpls));
100bd9d619aSnorby 	smpls = &sa_mpls;
101d90b9793Snorby 	smpls->smpls_family = AF_MPLS;
102d90b9793Snorby 	smpls->smpls_len = sizeof(*smpls);
10313a1ade2Smichele 	smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
104d90b9793Snorby 
10513a1ade2Smichele 	if (ntohl(smpls->smpls_label) < MPLS_LABEL_RESERVED_MAX) {
1061836b3f7Smichele 		m = mpls_shim_pop(m);
1075c53b932Sdlg 		if (m == NULL)
108b896ab8bSclaudio 			return;
1095b5e4895Sclaudio 		if (!hasbos) {
1108999a4d8Sclaudio 			/*
1118999a4d8Sclaudio 			 * RFC 4182 relaxes the position of the
1125b5e4895Sclaudio 			 * explicit NULL labels. They no longer need
1138999a4d8Sclaudio 			 * to be at the beginning of the stack.
1145b5e4895Sclaudio 			 * In this case the label is ignored and the decision
1155b5e4895Sclaudio 			 * is made based on the lower one.
1168999a4d8Sclaudio 			 */
1175b5e4895Sclaudio 			shim = mtod(m, struct shim_hdr *);
1185b5e4895Sclaudio 			smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
1195b5e4895Sclaudio 			hasbos = MPLS_BOS_ISSET(shim->shim_label);
1205b5e4895Sclaudio 		} else {
1215b5e4895Sclaudio 			switch (ntohl(smpls->smpls_label)) {
1225b5e4895Sclaudio 			case MPLS_LABEL_IPV4NULL:
1231d407297Sclaudio do_v4:
12448e57f09Sdlg 				if (mpls_mapttl_ip) {
12548e57f09Sdlg 					m = mpls_ip_adjttl(m, ttl);
12648e57f09Sdlg 					if (m == NULL)
1273f9b99eeSmpi 						return;
12848e57f09Sdlg 				}
1293f9b99eeSmpi 				ipv4_input(ifp, m);
1305b5e4895Sclaudio 				return;
13141a5659aSmikeb #ifdef INET6
1321836b3f7Smichele 			case MPLS_LABEL_IPV6NULL:
1331d407297Sclaudio do_v6:
13448e57f09Sdlg 				if (mpls_mapttl_ip6) {
13548e57f09Sdlg 					m = mpls_ip6_adjttl(m, ttl);
13648e57f09Sdlg 					if (m == NULL)
1373f9b99eeSmpi 						return;
13848e57f09Sdlg 				}
1393f9b99eeSmpi 				ipv6_input(ifp, m);
1405b5e4895Sclaudio 				return;
14141a5659aSmikeb #endif	/* INET6 */
1421d407297Sclaudio 			case MPLS_LABEL_IMPLNULL:
14358bcd087Sbluhm 				if (m->m_len < sizeof(u_char) &&
1445c53b932Sdlg 				    (m = m_pullup(m, sizeof(u_char))) == NULL)
14558bcd087Sbluhm 					return;
1461d407297Sclaudio 				switch (*mtod(m, u_char *) >> 4) {
1471d407297Sclaudio 				case IPVERSION:
1481d407297Sclaudio 					goto do_v4;
14941a5659aSmikeb #ifdef INET6
1501d407297Sclaudio 				case IPV6_VERSION >> 4:
1511d407297Sclaudio 					goto do_v6;
15241a5659aSmikeb #endif
153dedfd255Smichele 				default:
154dedfd255Smichele 					m_freem(m);
1555b5e4895Sclaudio 					return;
1561836b3f7Smichele 				}
1571d407297Sclaudio 			default:
1581836b3f7Smichele 				/* Other cases are not handled for now */
1591d407297Sclaudio 				m_freem(m);
1605b5e4895Sclaudio 				return;
1615b5e4895Sclaudio 			}
1621d407297Sclaudio 		}
1631836b3f7Smichele 	}
1645c53b932Sdlg 
165834dde95Sderaadt 	ifp = NULL;
1661836b3f7Smichele 
1676b0556cbSrenato 	rt = rtalloc(smplstosa(smpls), RT_RESOLVE, m->m_pkthdr.ph_rtableid);
168efb7c099Sdlg 	if (!rtisvalid(rt)) {
169d90b9793Snorby 		/* no entry for this label */
170d90b9793Snorby #ifdef MPLS_DEBUG
171d90b9793Snorby 		printf("MPLS_DEBUG: label not found\n");
172d90b9793Snorby #endif
173d90b9793Snorby 		m_freem(m);
1742f4366a8Smvs 		goto done;
175d90b9793Snorby 	}
176d90b9793Snorby 
177cd776ce1Smichele 	rt_mpls = (struct rt_mpls *)rt->rt_llinfo;
178cd776ce1Smichele 	if (rt_mpls == NULL || (rt->rt_flags & RTF_MPLS) == 0) {
179cd776ce1Smichele #ifdef MPLS_DEBUG
1805b5e4895Sclaudio 		printf("MPLS_DEBUG: no MPLS information attached\n");
181cd776ce1Smichele #endif
182cd776ce1Smichele 		m_freem(m);
183cd776ce1Smichele 		goto done;
184cd776ce1Smichele 	}
185cd776ce1Smichele 
186769a1feaSclaudio 	switch (rt_mpls->mpls_operation) {
187769a1feaSclaudio 	case MPLS_OP_POP:
18822b44b88Sdlg 		if (ISSET(rt->rt_flags, RTF_LOCAL)) {
18922b44b88Sdlg 			mpls_input_local(rt, m);
19022b44b88Sdlg 			goto done;
19122b44b88Sdlg 		}
19222b44b88Sdlg 
193dc9da2d3Smichele 		m = mpls_shim_pop(m);
194b896ab8bSclaudio 		if (m == NULL)
195b896ab8bSclaudio 			goto done;
196fd5d2b88Sclaudio 		if (!hasbos)
1975b5e4895Sclaudio 			/* just forward to gw */
198fd5d2b88Sclaudio 			break;
199fd5d2b88Sclaudio 
2005b5e4895Sclaudio 		/* last label popped so decide where to push it to */
2015b5e4895Sclaudio 		ifp = if_get(rt->rt_ifidx);
2025b5e4895Sclaudio 		if (ifp == NULL) {
2035b5e4895Sclaudio 			m_freem(m);
2045b5e4895Sclaudio 			goto done;
2055b5e4895Sclaudio 		}
2061ea7d866Srzalamena 
2075b5e4895Sclaudio 		KASSERT(rt->rt_gateway);
208fd5d2b88Sclaudio 
209fd5d2b88Sclaudio 		switch(rt->rt_gateway->sa_family) {
210fd5d2b88Sclaudio 		case AF_INET:
21125d240d2Sclaudio 			if ((m = mpls_ip_adjttl(m, ttl)) == NULL)
212fd5d2b88Sclaudio 				goto done;
213769a1feaSclaudio 			break;
21441a5659aSmikeb #ifdef INET6
215fd5d2b88Sclaudio 		case AF_INET6:
21625d240d2Sclaudio 			if ((m = mpls_ip6_adjttl(m, ttl)) == NULL)
217fd5d2b88Sclaudio 				goto done;
218fd5d2b88Sclaudio 			break;
21941a5659aSmikeb #endif
220d2788445Sdlg 		case AF_LINK:
221d2788445Sdlg 			break;
222fd5d2b88Sclaudio 		default:
223fd5d2b88Sclaudio 			m_freem(m);
224fd5d2b88Sclaudio 			goto done;
225fd5d2b88Sclaudio 		}
226fd5d2b88Sclaudio 
2275b5e4895Sclaudio 		/* shortcut sending out the packet */
2285b5e4895Sclaudio 		if (!ISSET(ifp->if_xflags, IFXF_MPLS))
2295b5e4895Sclaudio 			(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
2305b5e4895Sclaudio 		else
231fd5d2b88Sclaudio 			(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
232fd5d2b88Sclaudio 		goto done;
233769a1feaSclaudio 	case MPLS_OP_PUSH:
2345b5e4895Sclaudio 		/* this does not make much sense but it does not hurt */
235769a1feaSclaudio 		m = mpls_shim_push(m, rt_mpls);
236769a1feaSclaudio 		break;
237769a1feaSclaudio 	case MPLS_OP_SWAP:
238769a1feaSclaudio 		m = mpls_shim_swap(m, rt_mpls);
239769a1feaSclaudio 		break;
2405b5e4895Sclaudio 	default:
2415b5e4895Sclaudio 		m_freem(m);
2425b5e4895Sclaudio 		goto done;
243d90b9793Snorby 	}
244d90b9793Snorby 
245769a1feaSclaudio 	if (m == NULL)
246769a1feaSclaudio 		goto done;
247769a1feaSclaudio 
2485b5e4895Sclaudio 	/* refetch label and write back TTL */
249153b928aSclaudio 	shim = mtod(m, struct shim_hdr *);
2505b5e4895Sclaudio 	shim->shim_label = (shim->shim_label & ~MPLS_TTL_MASK) | htonl(ttl);
251153b928aSclaudio 
2525b5e4895Sclaudio 	ifp = if_get(rt->rt_ifidx);
2535b5e4895Sclaudio 	if (ifp == NULL) {
254cd776ce1Smichele 		m_freem(m);
255cd776ce1Smichele 		goto done;
256cd776ce1Smichele 	}
2576551b3dcSdlg #ifdef MPLS_DEBUG
258d90b9793Snorby 	printf("MPLS: sending on %s outlabel %x dst af %d in %d out %d\n",
259d90b9793Snorby     	    ifp->if_xname, ntohl(shim->shim_label), smpls->smpls_family,
26013a1ade2Smichele 	    MPLS_LABEL_GET(smpls->smpls_label),
261cd776ce1Smichele 	    MPLS_LABEL_GET(rt_mpls->mpls_label));
2626551b3dcSdlg #endif
263d90b9793Snorby 
264769a1feaSclaudio 	/* Output iface is not MPLS-enabled */
265769a1feaSclaudio 	if (!ISSET(ifp->if_xflags, IFXF_MPLS)) {
266769a1feaSclaudio #ifdef MPLS_DEBUG
2675b5e4895Sclaudio 		printf("MPLS_DEBUG: interface %s not mpls enabled\n",
2685b5e4895Sclaudio 		    ifp->if_xname);
269769a1feaSclaudio #endif
2705b5e4895Sclaudio 		m_freem(m);
271769a1feaSclaudio 		goto done;
272769a1feaSclaudio 	}
273769a1feaSclaudio 
274769a1feaSclaudio 	(*ifp->if_ll_output)(ifp, m, smplstosa(smpls), rt);
275d90b9793Snorby done:
2765b5e4895Sclaudio 	if_put(ifp);
27727ae666cSmpi 	rtfree(rt);
278d90b9793Snorby }
279dedfd255Smichele 
28022b44b88Sdlg void
mpls_input_local(struct rtentry * rt,struct mbuf * m)28122b44b88Sdlg mpls_input_local(struct rtentry *rt, struct mbuf *m)
28222b44b88Sdlg {
28322b44b88Sdlg 	struct ifnet *ifp;
28422b44b88Sdlg 
28522b44b88Sdlg 	ifp = if_get(rt->rt_ifidx);
28622b44b88Sdlg 	if (ifp == NULL) {
28722b44b88Sdlg 		m_freem(m);
28822b44b88Sdlg 		return;
28922b44b88Sdlg 	}
29022b44b88Sdlg 
29122b44b88Sdlg 	/* shortcut sending out the packet */
29222b44b88Sdlg 	if (!ISSET(ifp->if_xflags, IFXF_MPLS))
29322b44b88Sdlg 		(*ifp->if_output)(ifp, m, rt->rt_gateway, rt);
29422b44b88Sdlg 	else
29522b44b88Sdlg 		(*ifp->if_ll_output)(ifp, m, rt->rt_gateway, rt);
29622b44b88Sdlg 
29722b44b88Sdlg 	if_put(ifp);
29822b44b88Sdlg }
29922b44b88Sdlg 
30025d240d2Sclaudio struct mbuf *
mpls_ip_adjttl(struct mbuf * m,u_int8_t ttl)301fd5d2b88Sclaudio mpls_ip_adjttl(struct mbuf *m, u_int8_t ttl)
302dedfd255Smichele {
303dedfd255Smichele 	struct ip *ip;
304f1859ff1Sprocter 	uint16_t old, new;
305f1859ff1Sprocter 	uint32_t x;
306dedfd255Smichele 
30748e57f09Sdlg 	if (m->m_len < sizeof(*ip)) {
30848e57f09Sdlg 		m = m_pullup(m, sizeof(*ip));
30948e57f09Sdlg 		if (m == NULL)
31048e57f09Sdlg 			return (NULL);
311dedfd255Smichele 	}
31248e57f09Sdlg 	ip = mtod(m, struct ip *);
313dedfd255Smichele 
31448e57f09Sdlg 	old = htons(ip->ip_ttl << 8);
31548e57f09Sdlg 	new = htons(ttl << 8);
31648e57f09Sdlg 	x = ip->ip_sum + old - new;
31748e57f09Sdlg 
318dedfd255Smichele 	ip->ip_ttl = ttl;
319f1859ff1Sprocter 	/* see pf_cksum_fixup() */
32048e57f09Sdlg 	ip->ip_sum = (x) + (x >> 16);
321dedfd255Smichele 
32248e57f09Sdlg 	return (m);
323dedfd255Smichele }
324dedfd255Smichele 
32541a5659aSmikeb #ifdef INET6
32625d240d2Sclaudio struct mbuf *
mpls_ip6_adjttl(struct mbuf * m,u_int8_t ttl)327fd5d2b88Sclaudio mpls_ip6_adjttl(struct mbuf *m, u_int8_t ttl)
328dedfd255Smichele {
32948e57f09Sdlg 	struct ip6_hdr *ip6;
330dedfd255Smichele 
33148e57f09Sdlg 	if (m->m_len < sizeof(*ip6)) {
33248e57f09Sdlg 		m = m_pullup(m, sizeof(*ip6));
33348e57f09Sdlg 		if (m == NULL)
33448e57f09Sdlg 			return (NULL);
335dedfd255Smichele 	}
33648e57f09Sdlg 	ip6 = mtod(m, struct ip6_hdr *);
33748e57f09Sdlg 
33848e57f09Sdlg 	ip6->ip6_hlim = ttl;
33948e57f09Sdlg 
34048e57f09Sdlg 	return (m);
341dedfd255Smichele }
34241a5659aSmikeb #endif	/* INET6 */
343201d6983Sclaudio 
344201d6983Sclaudio struct mbuf *
mpls_do_error(struct mbuf * m,int type,int code,int destmtu)345201d6983Sclaudio mpls_do_error(struct mbuf *m, int type, int code, int destmtu)
346201d6983Sclaudio {
347201d6983Sclaudio 	struct shim_hdr stack[MPLS_INKERNEL_LOOP_MAX];
348201d6983Sclaudio 	struct sockaddr_mpls sa_mpls;
349201d6983Sclaudio 	struct sockaddr_mpls *smpls;
350201d6983Sclaudio 	struct rtentry *rt = NULL;
351201d6983Sclaudio 	struct shim_hdr *shim;
352201d6983Sclaudio 	struct in_ifaddr *ia;
353201d6983Sclaudio 	struct icmp *icp;
354201d6983Sclaudio 	struct ip *ip;
355b9eec9beSmpi 	int nstk, error;
356201d6983Sclaudio 
357201d6983Sclaudio 	for (nstk = 0; nstk < MPLS_INKERNEL_LOOP_MAX; nstk++) {
358201d6983Sclaudio 		if (m->m_len < sizeof(*shim) &&
35958bcd087Sbluhm 		    (m = m_pullup(m, sizeof(*shim))) == NULL)
360201d6983Sclaudio 			return (NULL);
361201d6983Sclaudio 		stack[nstk] = *mtod(m, struct shim_hdr *);
362201d6983Sclaudio 		m_adj(m, sizeof(*shim));
363201d6983Sclaudio 		if (MPLS_BOS_ISSET(stack[nstk].shim_label))
364201d6983Sclaudio 			break;
365201d6983Sclaudio 	}
366201d6983Sclaudio 	shim = &stack[0];
367201d6983Sclaudio 
36858bcd087Sbluhm 	if (m->m_len < sizeof(u_char) &&
36958bcd087Sbluhm 	    (m = m_pullup(m, sizeof(u_char))) == NULL)
37058bcd087Sbluhm 		return (NULL);
371201d6983Sclaudio 	switch (*mtod(m, u_char *) >> 4) {
372201d6983Sclaudio 	case IPVERSION:
373201d6983Sclaudio 		if (m->m_len < sizeof(*ip) &&
374201d6983Sclaudio 		    (m = m_pullup(m, sizeof(*ip))) == NULL)
375201d6983Sclaudio 			return (NULL);
376201d6983Sclaudio 		m = icmp_do_error(m, type, code, 0, destmtu);
377201d6983Sclaudio 		if (m == NULL)
378201d6983Sclaudio 			return (NULL);
379201d6983Sclaudio 
380201d6983Sclaudio 		if (icmp_do_exthdr(m, ICMP_EXT_MPLS, 1, stack,
381201d6983Sclaudio 		    (nstk + 1) * sizeof(*shim)))
382201d6983Sclaudio 			return (NULL);
383201d6983Sclaudio 
384201d6983Sclaudio 		/* set ip_src to something usable, based on the MPLS label */
385201d6983Sclaudio 		bzero(&sa_mpls, sizeof(sa_mpls));
386201d6983Sclaudio 		smpls = &sa_mpls;
387201d6983Sclaudio 		smpls->smpls_family = AF_MPLS;
388201d6983Sclaudio 		smpls->smpls_len = sizeof(*smpls);
389201d6983Sclaudio 		smpls->smpls_label = shim->shim_label & MPLS_LABEL_MASK;
390201d6983Sclaudio 
391d978c1c3Sclaudio 		rt = rtalloc(smplstosa(smpls), RT_RESOLVE, 0);
392efb7c099Sdlg 		if (!rtisvalid(rt)) {
393efb7c099Sdlg 			rtfree(rt);
394201d6983Sclaudio 			/* no entry for this label */
395201d6983Sclaudio 			m_freem(m);
396201d6983Sclaudio 			return (NULL);
397201d6983Sclaudio 		}
3988671bacfSmpi 		if (rt->rt_ifa->ifa_addr->sa_family == AF_INET)
399201d6983Sclaudio 			ia = ifatoia(rt->rt_ifa);
400201d6983Sclaudio 		else {
401201d6983Sclaudio 			/* XXX this needs fixing, if the MPLS is on an IP
402201d6983Sclaudio 			 * less interface we need to find some other IP to
403201d6983Sclaudio 			 * use as source.
404201d6983Sclaudio 			 */
40527ae666cSmpi 			rtfree(rt);
406201d6983Sclaudio 			m_freem(m);
407201d6983Sclaudio 			return (NULL);
408201d6983Sclaudio 		}
409e39b1741Smpi 		/* It is safe to dereference ``ia'' iff ``rt'' is valid. */
410b9eec9beSmpi 		error = icmp_reflect(m, NULL, ia);
411e39b1741Smpi 		rtfree(rt);
412b9eec9beSmpi 		if (error)
413201d6983Sclaudio 			return (NULL);
414201d6983Sclaudio 
415201d6983Sclaudio 		ip = mtod(m, struct ip *);
416678831beSjsg 		/* stuff to fix up which is normally done in ip_output */
417201d6983Sclaudio 		ip->ip_v = IPVERSION;
418201d6983Sclaudio 		ip->ip_id = htons(ip_randomid());
419*55055d61Sbluhm 		in_hdr_cksum_out(m, NULL);
420201d6983Sclaudio 
421201d6983Sclaudio 		/* stolen from icmp_send() */
4220c0a686aSmpi 		icp = (struct icmp *)(mtod(m, caddr_t) + sizeof(*ip));
423201d6983Sclaudio 		icp->icmp_cksum = 0;
4240c0a686aSmpi 		icp->icmp_cksum = in4_cksum(m, 0, sizeof(*ip),
4250c0a686aSmpi 		    ntohs(ip->ip_len) - sizeof(*ip));
426201d6983Sclaudio 
427201d6983Sclaudio 		break;
42841a5659aSmikeb #ifdef INET6
429201d6983Sclaudio 	case IPV6_VERSION >> 4:
43041a5659aSmikeb #endif
431201d6983Sclaudio 	default:
432201d6983Sclaudio 		m_freem(m);
433201d6983Sclaudio 		return (NULL);
434201d6983Sclaudio 	}
435201d6983Sclaudio 
436201d6983Sclaudio 	/* add mpls stack back to new packet */
437201d6983Sclaudio 	M_PREPEND(m, (nstk + 1) * sizeof(*shim), M_NOWAIT);
438201d6983Sclaudio 	if (m == NULL)
439201d6983Sclaudio 		return (NULL);
440201d6983Sclaudio 	m_copyback(m, 0, (nstk + 1) * sizeof(*shim), stack, M_NOWAIT);
441201d6983Sclaudio 
442201d6983Sclaudio 	/* change TTL to default */
443201d6983Sclaudio 	shim = mtod(m, struct shim_hdr *);
444201d6983Sclaudio 	shim->shim_label =
445201d6983Sclaudio 	    (shim->shim_label & ~MPLS_TTL_MASK) | htonl(mpls_defttl);
446201d6983Sclaudio 
447201d6983Sclaudio 	return (m);
448201d6983Sclaudio }
449