xref: /openbsd-src/sys/net/if_mpw.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: if_mpw.c,v 1.62 2021/03/26 19:00:21 kn Exp $ */
2 
3 /*
4  * Copyright (c) 2015 Rafael Zalamena <rzalamena@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 "bpfilter.h"
20 #include "vlan.h"
21 
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/mbuf.h>
25 #include <sys/socket.h>
26 #include <sys/ioctl.h>
27 #include <sys/errno.h>
28 
29 #include <net/if.h>
30 #include <net/if_dl.h>
31 #include <net/if_types.h>
32 #include <net/route.h>
33 
34 #include <netinet/in.h>
35 
36 #include <netinet/if_ether.h>
37 #include <netmpls/mpls.h>
38 
39 #if NBPFILTER > 0
40 #include <net/bpf.h>
41 #endif /* NBPFILTER */
42 
43 #if NVLAN > 0
44 #include <net/if_vlan_var.h>
45 #endif
46 
47 struct mpw_neighbor {
48 	struct shim_hdr		n_rshim;
49 	struct sockaddr_storage	n_nexthop;
50 };
51 
52 struct mpw_softc {
53 	struct arpcom		sc_ac;
54 #define sc_if			sc_ac.ac_if
55 
56 	int			sc_txhprio;
57 	int			sc_rxhprio;
58 	unsigned int		sc_rdomain;
59 	struct ifaddr		sc_ifa;
60 	struct sockaddr_mpls	sc_smpls; /* Local label */
61 
62 	unsigned int		sc_cword;
63 	unsigned int		sc_fword;
64 	uint32_t		sc_flow;
65 	uint32_t		sc_type;
66 	struct mpw_neighbor	*sc_neighbor;
67 
68 	unsigned int		sc_dead;
69 };
70 
71 void	mpwattach(int);
72 int	mpw_clone_create(struct if_clone *, int);
73 int	mpw_clone_destroy(struct ifnet *);
74 int	mpw_ioctl(struct ifnet *, u_long, caddr_t);
75 int	mpw_output(struct ifnet *, struct mbuf *, struct sockaddr *,
76     struct rtentry *);
77 void	mpw_start(struct ifnet *);
78 #if NVLAN > 0
79 struct	mbuf *mpw_vlan_handle(struct mbuf *, struct mpw_softc *);
80 #endif /* NVLAN */
81 
82 struct if_clone mpw_cloner =
83     IF_CLONE_INITIALIZER("mpw", mpw_clone_create, mpw_clone_destroy);
84 
85 void
86 mpwattach(int n)
87 {
88 	if_clone_attach(&mpw_cloner);
89 }
90 
91 int
92 mpw_clone_create(struct if_clone *ifc, int unit)
93 {
94 	struct mpw_softc *sc;
95 	struct ifnet *ifp;
96 
97 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
98 	if (sc == NULL)
99 		return (ENOMEM);
100 
101 	sc->sc_flow = arc4random();
102 	sc->sc_neighbor = NULL;
103 
104 	ifp = &sc->sc_if;
105 	snprintf(ifp->if_xname, sizeof(ifp->if_xname), "%s%d",
106 	    ifc->ifc_name, unit);
107 	ifp->if_softc = sc;
108 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
109 	ifp->if_xflags = IFXF_CLONED;
110 	ifp->if_ioctl = mpw_ioctl;
111 	ifp->if_output = mpw_output;
112 	ifp->if_start = mpw_start;
113 	ifp->if_hardmtu = ETHER_MAX_HARDMTU_LEN;
114 	ether_fakeaddr(ifp);
115 
116 	sc->sc_dead = 0;
117 
118 	if_counters_alloc(ifp);
119 	if_attach(ifp);
120 	ether_ifattach(ifp);
121 
122 	sc->sc_txhprio = 0;
123 	sc->sc_rxhprio = IF_HDRPRIO_PACKET;
124 	sc->sc_rdomain = 0;
125 	sc->sc_ifa.ifa_ifp = ifp;
126 	sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
127 	sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
128 	sc->sc_smpls.smpls_family = AF_MPLS;
129 
130 	return (0);
131 }
132 
133 int
134 mpw_clone_destroy(struct ifnet *ifp)
135 {
136 	struct mpw_softc *sc = ifp->if_softc;
137 
138 	NET_LOCK();
139 	ifp->if_flags &= ~IFF_RUNNING;
140 	sc->sc_dead = 1;
141 
142 	if (sc->sc_smpls.smpls_label) {
143 		rt_ifa_del(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
144 		    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
145 	}
146 	NET_UNLOCK();
147 
148 	ifq_barrier(&ifp->if_snd);
149 
150 	ether_ifdetach(ifp);
151 	if_detach(ifp);
152 
153 	free(sc->sc_neighbor, M_DEVBUF, sizeof(*sc->sc_neighbor));
154 	free(sc, M_DEVBUF, sizeof(*sc));
155 
156 	return (0);
157 }
158 
159 int
160 mpw_set_route(struct mpw_softc *sc, uint32_t label, unsigned int rdomain)
161 {
162 	int error;
163 
164 	if (sc->sc_dead)
165 		return (ENXIO);
166 
167 	if (sc->sc_smpls.smpls_label) {
168 		rt_ifa_del(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
169 		    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
170 	}
171 
172 	sc->sc_smpls.smpls_label = label;
173 	sc->sc_rdomain = rdomain;
174 
175 	/* only install with a label or mpw_clone_destroy() will ignore it */
176 	if (sc->sc_smpls.smpls_label == MPLS_LABEL2SHIM(0))
177 		return 0;
178 
179 	error = rt_ifa_add(&sc->sc_ifa, RTF_MPLS|RTF_LOCAL,
180 	    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
181 	if (error != 0)
182 		sc->sc_smpls.smpls_label = 0;
183 
184 	return (error);
185 }
186 
187 static int
188 mpw_set_neighbor(struct mpw_softc *sc, const struct if_laddrreq *req)
189 {
190 	struct mpw_neighbor *n, *o;
191 	const struct sockaddr_storage *ss;
192 	const struct sockaddr_mpls *smpls;
193 	uint32_t label;
194 
195 	smpls = (const struct sockaddr_mpls *)&req->dstaddr;
196 
197 	if (smpls->smpls_family != AF_MPLS)
198 		return (EINVAL);
199 	label = smpls->smpls_label;
200 	if (label > MPLS_LABEL_MAX || label <= MPLS_LABEL_RESERVED_MAX)
201 		return (EINVAL);
202 
203 	ss = &req->addr;
204 	switch (ss->ss_family) {
205 	case AF_INET: {
206 		const struct sockaddr_in *sin =
207 		    (const struct sockaddr_in *)ss;
208 
209 		if (in_nullhost(sin->sin_addr) ||
210 		    IN_MULTICAST(sin->sin_addr.s_addr))
211 			return (EINVAL);
212 
213 		break;
214 	}
215 #ifdef INET6
216 	case AF_INET6: {
217 		const struct sockaddr_in6 *sin6 =
218 		    (const struct sockaddr_in6 *)ss;
219 
220 		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr) ||
221 		    IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
222 			return (EINVAL);
223 
224 		/* check scope */
225 
226 		break;
227 	}
228 #endif
229 	default:
230 		return (EAFNOSUPPORT);
231 	}
232 
233 	if (sc->sc_dead)
234 		return (ENXIO);
235 
236 	n = malloc(sizeof(*n), M_DEVBUF, M_WAITOK|M_CANFAIL|M_ZERO);
237 	if (n == NULL)
238 		return (ENOMEM);
239 
240 	n->n_rshim.shim_label = MPLS_LABEL2SHIM(label);
241 	n->n_nexthop = *ss;
242 
243 	o = sc->sc_neighbor;
244 	sc->sc_neighbor = n;
245 
246 	NET_UNLOCK();
247 	ifq_barrier(&sc->sc_if.if_snd);
248 	NET_LOCK();
249 
250 	free(o, M_DEVBUF, sizeof(*o));
251 
252 	return (0);
253 }
254 
255 static int
256 mpw_get_neighbor(struct mpw_softc *sc, struct if_laddrreq *req)
257 {
258 	struct sockaddr_mpls *smpls = (struct sockaddr_mpls *)&req->dstaddr;
259 	struct mpw_neighbor *n = sc->sc_neighbor;
260 
261 	if (n == NULL)
262 		return (EADDRNOTAVAIL);
263 
264 	smpls->smpls_len = sizeof(*smpls);
265 	smpls->smpls_family = AF_MPLS;
266 	smpls->smpls_label = MPLS_SHIM2LABEL(n->n_rshim.shim_label);
267 
268 	req->addr = n->n_nexthop;
269 
270 	return (0);
271 }
272 
273 static int
274 mpw_del_neighbor(struct mpw_softc *sc)
275 {
276 	struct mpw_neighbor *o;
277 
278 	if (sc->sc_dead)
279 		return (ENXIO);
280 
281 	o = sc->sc_neighbor;
282 	sc->sc_neighbor = NULL;
283 
284 	NET_UNLOCK();
285 	ifq_barrier(&sc->sc_if.if_snd);
286 	NET_LOCK();
287 
288 	free(o, M_DEVBUF, sizeof(*o));
289 
290 	return (0);
291 }
292 
293 static int
294 mpw_set_label(struct mpw_softc *sc, const struct shim_hdr *label)
295 {
296 	uint32_t shim;
297 
298 	if (label->shim_label > MPLS_LABEL_MAX ||
299 	    label->shim_label <= MPLS_LABEL_RESERVED_MAX)
300 		return (EINVAL);
301 
302 	shim = MPLS_LABEL2SHIM(label->shim_label);
303 	if (sc->sc_smpls.smpls_label == shim)
304 		return (0);
305 
306 	return (mpw_set_route(sc, shim, sc->sc_rdomain));
307 }
308 
309 static int
310 mpw_get_label(struct mpw_softc *sc, struct ifreq *ifr)
311 {
312 	struct shim_hdr label;
313 
314 	label.shim_label = MPLS_SHIM2LABEL(sc->sc_smpls.smpls_label);
315 
316 	if (label.shim_label == 0)
317 		return (EADDRNOTAVAIL);
318 
319 	return (copyout(&label, ifr->ifr_data, sizeof(label)));
320 }
321 
322 static int
323 mpw_del_label(struct mpw_softc *sc)
324 {
325 	if (sc->sc_dead)
326 		return (ENXIO);
327 
328 	if (sc->sc_smpls.smpls_label != MPLS_LABEL2SHIM(0)) {
329 		rt_ifa_del(&sc->sc_ifa, RTF_MPLS | RTF_LOCAL,
330 		    smplstosa(&sc->sc_smpls), sc->sc_rdomain);
331 	}
332 
333 	sc->sc_smpls.smpls_label = MPLS_LABEL2SHIM(0);
334 
335 	return (0);
336 }
337 
338 static int
339 mpw_set_config(struct mpw_softc *sc, const struct ifreq *ifr)
340 {
341 	struct ifmpwreq imr;
342 	struct if_laddrreq req;
343 	struct sockaddr_mpls *smpls;
344 	struct sockaddr_in *sin;
345 	int error;
346 
347 	error = copyin(ifr->ifr_data, &imr, sizeof(imr));
348 	if (error != 0)
349 		return (error);
350 
351 	/* Teardown all configuration if got no nexthop */
352 	sin = (struct sockaddr_in *)&imr.imr_nexthop;
353 	if (sin->sin_addr.s_addr == 0) {
354 		mpw_del_label(sc);
355 		mpw_del_neighbor(sc);
356 		sc->sc_cword = 0;
357 		sc->sc_type = 0;
358 		return (0);
359 	}
360 
361 	error = mpw_set_label(sc, &imr.imr_lshim);
362 	if (error != 0)
363 		return (error);
364 
365 	smpls = (struct sockaddr_mpls *)&req.dstaddr;
366 	smpls->smpls_family = AF_MPLS;
367 	smpls->smpls_label = imr.imr_rshim.shim_label;
368 	req.addr = imr.imr_nexthop;
369 
370 	error = mpw_set_neighbor(sc, &req);
371 	if (error != 0)
372 		return (error);
373 
374 	sc->sc_cword = ISSET(imr.imr_flags, IMR_FLAG_CONTROLWORD);
375 	sc->sc_type = imr.imr_type;
376 
377 	return (0);
378 }
379 
380 static int
381 mpw_get_config(struct mpw_softc *sc, const struct ifreq *ifr)
382 {
383 	struct ifmpwreq imr;
384 
385 	memset(&imr, 0, sizeof(imr));
386 	imr.imr_flags = sc->sc_cword ? IMR_FLAG_CONTROLWORD : 0;
387 	imr.imr_type = sc->sc_type;
388 
389 	imr.imr_lshim.shim_label = MPLS_SHIM2LABEL(sc->sc_smpls.smpls_label);
390 	if (sc->sc_neighbor) {
391 		imr.imr_rshim.shim_label =
392 		    MPLS_SHIM2LABEL(sc->sc_neighbor->n_rshim.shim_label);
393 		imr.imr_nexthop = sc->sc_neighbor->n_nexthop;
394 	}
395 
396 	return (copyout(&imr, ifr->ifr_data, sizeof(imr)));
397 }
398 
399 int
400 mpw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
401 {
402 	struct ifreq *ifr = (struct ifreq *) data;
403 	struct mpw_softc *sc = ifp->if_softc;
404 	struct shim_hdr shim;
405 	int error = 0;
406 
407 	switch (cmd) {
408 	case SIOCSIFFLAGS:
409 		if ((ifp->if_flags & IFF_UP))
410 			ifp->if_flags |= IFF_RUNNING;
411 		else
412 			ifp->if_flags &= ~IFF_RUNNING;
413 		break;
414 
415 	case SIOCGPWE3:
416 		ifr->ifr_pwe3 = IF_PWE3_ETHERNET;
417 		break;
418 	case SIOCSPWE3CTRLWORD:
419 		sc->sc_cword = ifr->ifr_pwe3 ? 1 : 0;
420 		break;
421 	case SIOCGPWE3CTRLWORD:
422 		ifr->ifr_pwe3 = sc->sc_cword;
423 		break;
424 	case SIOCSPWE3FAT:
425 		sc->sc_fword = ifr->ifr_pwe3 ? 1 : 0;
426 		break;
427 	case SIOCGPWE3FAT:
428 		ifr->ifr_pwe3 = sc->sc_fword;
429 		break;
430 
431 	case SIOCSPWE3NEIGHBOR:
432 		error = mpw_set_neighbor(sc, (struct if_laddrreq *)data);
433 		break;
434 	case SIOCGPWE3NEIGHBOR:
435 		error = mpw_get_neighbor(sc, (struct if_laddrreq *)data);
436 		break;
437 	case SIOCDPWE3NEIGHBOR:
438 		error = mpw_del_neighbor(sc);
439 		break;
440 
441 	case SIOCGETLABEL:
442 		error = mpw_get_label(sc, ifr);
443 		break;
444 	case SIOCSETLABEL:
445 		error = copyin(ifr->ifr_data, &shim, sizeof(shim));
446 		if (error != 0)
447 			break;
448 		error = mpw_set_label(sc, &shim);
449 		break;
450 	case SIOCDELLABEL:
451 		error = mpw_del_label(sc);
452 		break;
453 
454 	case SIOCSETMPWCFG:
455 		error = mpw_set_config(sc, ifr);
456 		break;
457 
458 	case SIOCGETMPWCFG:
459 		error = mpw_get_config(sc, ifr);
460 		break;
461 
462 	case SIOCSLIFPHYRTABLE:
463 		if (ifr->ifr_rdomainid < 0 ||
464 		    ifr->ifr_rdomainid > RT_TABLEID_MAX ||
465 		    !rtable_exists(ifr->ifr_rdomainid) ||
466 		    ifr->ifr_rdomainid != rtable_l2(ifr->ifr_rdomainid)) {
467 			error = EINVAL;
468 			break;
469 		}
470 		if (sc->sc_rdomain != ifr->ifr_rdomainid) {
471 			error = mpw_set_route(sc, sc->sc_smpls.smpls_label,
472 			    ifr->ifr_rdomainid);
473 		}
474 		break;
475 	case SIOCGLIFPHYRTABLE:
476 		ifr->ifr_rdomainid = sc->sc_rdomain;
477 		break;
478 
479 	case SIOCSTXHPRIO:
480 		error = if_txhprio_l2_check(ifr->ifr_hdrprio);
481 		if (error != 0)
482 			break;
483 
484 		sc->sc_txhprio = ifr->ifr_hdrprio;
485 		break;
486 	case SIOCGTXHPRIO:
487 		ifr->ifr_hdrprio = sc->sc_txhprio;
488 		break;
489 
490 	case SIOCSRXHPRIO:
491 		error = if_rxhprio_l2_check(ifr->ifr_hdrprio);
492 		if (error != 0)
493 			break;
494 
495 		sc->sc_rxhprio = ifr->ifr_hdrprio;
496 		break;
497 	case SIOCGRXHPRIO:
498 		ifr->ifr_hdrprio = sc->sc_rxhprio;
499 		break;
500 
501 	case SIOCADDMULTI:
502 	case SIOCDELMULTI:
503 		break;
504 
505 	default:
506 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
507 		break;
508 	}
509 
510 	return (error);
511 }
512 
513 static void
514 mpw_input(struct mpw_softc *sc, struct mbuf *m)
515 {
516 	struct ifnet *ifp = &sc->sc_if;
517 	struct shim_hdr *shim;
518 	struct mbuf *n;
519 	uint32_t exp;
520 	int rxprio;
521 	int off;
522 
523 	if (!ISSET(ifp->if_flags, IFF_RUNNING))
524 		goto drop;
525 
526 	shim = mtod(m, struct shim_hdr *);
527 	exp = ntohl(shim->shim_label & MPLS_EXP_MASK) >> MPLS_EXP_OFFSET;
528 	if (sc->sc_fword) {
529 		uint32_t flow;
530 
531 		if (MPLS_BOS_ISSET(shim->shim_label))
532 			goto drop;
533 		m_adj(m, sizeof(*shim));
534 
535 		if (m->m_len < sizeof(*shim)) {
536 			m = m_pullup(m, sizeof(*shim));
537 			if (m == NULL)
538 				return;
539 		}
540 		shim = mtod(m, struct shim_hdr *);
541 
542 		if (!MPLS_BOS_ISSET(shim->shim_label))
543 			goto drop;
544 
545 		flow = MPLS_SHIM2LABEL(shim->shim_label);
546 		flow ^= sc->sc_flow;
547 		SET(m->m_pkthdr.csum_flags, M_FLOWID);
548 		m->m_pkthdr.ph_flowid = flow;
549 	} else {
550 		if (!MPLS_BOS_ISSET(shim->shim_label))
551 			goto drop;
552 	}
553 	m_adj(m, sizeof(*shim));
554 
555 	if (sc->sc_cword) {
556 		if (m->m_len < sizeof(*shim)) {
557 			m = m_pullup(m, sizeof(*shim));
558 			if (m == NULL)
559 				return;
560 		}
561 		shim = mtod(m, struct shim_hdr *);
562 
563 		/*
564 		 * The first 4 bits identifies that this packet is a
565 		 * control word. If the control word is configured and
566 		 * we received an IP datagram we shall drop it.
567 		 */
568 		if (shim->shim_label & CW_ZERO_MASK) {
569 			ifp->if_ierrors++;
570 			goto drop;
571 		}
572 
573 		/* We don't support fragmentation just yet. */
574 		if (shim->shim_label & CW_FRAG_MASK) {
575 			ifp->if_ierrors++;
576 			goto drop;
577 		}
578 
579 		m_adj(m, MPLS_HDRLEN);
580 	}
581 
582 	if (m->m_len < sizeof(struct ether_header)) {
583 		m = m_pullup(m, sizeof(struct ether_header));
584 		if (m == NULL)
585 			return;
586 	}
587 
588 	n = m_getptr(m, sizeof(struct ether_header), &off);
589 	if (n == NULL) {
590 		ifp->if_ierrors++;
591 		goto drop;
592 	}
593 	if (!ALIGNED_POINTER(mtod(n, caddr_t) + off, uint32_t)) {
594 		n = m_dup_pkt(m, ETHER_ALIGN, M_NOWAIT);
595 		/* Dispose of the original mbuf chain */
596 		m_freem(m);
597 		if (n == NULL)
598 			return;
599 		m = n;
600 	}
601 
602 	rxprio = sc->sc_rxhprio;
603 	switch (rxprio) {
604 	case IF_HDRPRIO_PACKET:
605 		/* nop */
606 		break;
607 	case IF_HDRPRIO_OUTER:
608 		m->m_pkthdr.pf.prio = exp;
609 		break;
610 	default:
611 		m->m_pkthdr.pf.prio = rxprio;
612 		break;
613 	}
614 
615 	m->m_pkthdr.ph_ifidx = ifp->if_index;
616 	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
617 
618 	/* packet has not been processed by PF yet. */
619 	KASSERT(m->m_pkthdr.pf.statekey == NULL);
620 
621 	if_vinput(ifp, m);
622 	return;
623 drop:
624 	m_freem(m);
625 }
626 
627 int
628 mpw_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
629     struct rtentry *rt)
630 {
631 	struct mpw_softc *sc = ifp->if_softc;
632 
633 	if (dst->sa_family == AF_LINK &&
634 	    rt != NULL && ISSET(rt->rt_flags, RTF_LOCAL)) {
635 		mpw_input(sc, m);
636 		return (0);
637 	}
638 
639 	return (ether_output(ifp, m, dst, rt));
640 }
641 
642 void
643 mpw_start(struct ifnet *ifp)
644 {
645 	struct mpw_softc *sc = ifp->if_softc;
646 	struct rtentry *rt;
647 	struct ifnet *ifp0;
648 	struct mbuf *m, *m0;
649 	struct shim_hdr *shim;
650 	struct mpw_neighbor *n;
651 	struct sockaddr_mpls smpls = {
652 		.smpls_len = sizeof(smpls),
653 		.smpls_family = AF_MPLS,
654 	};
655 	int txprio = sc->sc_txhprio;
656 	uint8_t prio;
657 	uint32_t exp, bos;
658 
659 	n = sc->sc_neighbor;
660 	if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
661 	    n == NULL) {
662 		ifq_purge(&ifp->if_snd);
663 		return;
664 	}
665 
666 	rt = rtalloc(sstosa(&n->n_nexthop), RT_RESOLVE, sc->sc_rdomain);
667 	if (!rtisvalid(rt)) {
668 		ifq_purge(&ifp->if_snd);
669 		goto rtfree;
670 	}
671 
672 	ifp0 = if_get(rt->rt_ifidx);
673 	if (ifp0 == NULL) {
674 		ifq_purge(&ifp->if_snd);
675 		goto rtfree;
676 	}
677 
678 	while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
679 #if NBPFILTER > 0
680 		if (sc->sc_if.if_bpf)
681 			bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_OUT);
682 #endif /* NBPFILTER */
683 
684 		m0 = m_get(M_DONTWAIT, m->m_type);
685 		if (m0 == NULL) {
686 			m_freem(m);
687 			continue;
688 		}
689 
690 		M_MOVE_PKTHDR(m0, m);
691 		m0->m_next = m;
692 		m_align(m0, 0);
693 		m0->m_len = 0;
694 
695 		if (sc->sc_cword) {
696 			m0 = m_prepend(m0, sizeof(*shim), M_NOWAIT);
697 			if (m0 == NULL)
698 				continue;
699 
700 			shim = mtod(m0, struct shim_hdr *);
701 			memset(shim, 0, sizeof(*shim));
702 		}
703 
704 		switch (txprio) {
705 		case IF_HDRPRIO_PACKET:
706 			prio = m->m_pkthdr.pf.prio;
707 			break;
708 		default:
709 			prio = txprio;
710 			break;
711 		}
712 		exp = htonl(prio << MPLS_EXP_OFFSET);
713 
714 		bos = MPLS_BOS_MASK;
715 		if (sc->sc_fword) {
716 			uint32_t flow = sc->sc_flow;
717 
718 			m0 = m_prepend(m0, sizeof(*shim), M_NOWAIT);
719 			if (m0 == NULL)
720 				continue;
721 
722 			if (ISSET(m->m_pkthdr.csum_flags, M_FLOWID))
723 				flow ^= m->m_pkthdr.ph_flowid;
724 
725 			shim = mtod(m0, struct shim_hdr *);
726 			shim->shim_label = htonl(1) & MPLS_TTL_MASK;
727 			shim->shim_label |= MPLS_LABEL2SHIM(flow) | exp | bos;
728 
729 			bos = 0;
730 		}
731 
732 		m0 = m_prepend(m0, sizeof(*shim), M_NOWAIT);
733 		if (m0 == NULL)
734 			continue;
735 
736 		shim = mtod(m0, struct shim_hdr *);
737 		shim->shim_label = htonl(mpls_defttl) & MPLS_TTL_MASK;
738 		shim->shim_label |= n->n_rshim.shim_label | exp | bos;
739 
740 		m0->m_pkthdr.ph_rtableid = sc->sc_rdomain;
741 		CLR(m0->m_flags, M_BCAST|M_MCAST);
742 
743 		mpls_output(ifp0, m0, (struct sockaddr *)&smpls, rt);
744 	}
745 
746 	if_put(ifp0);
747 rtfree:
748 	rtfree(rt);
749 }
750