1*63f0d21cSthorpej /* $NetBSD: if_mpls.c,v 1.41 2022/09/03 20:29:31 thorpej Exp $ */
2826653c1Skefren
3826653c1Skefren /*
4826653c1Skefren * Copyright (c) 2010 The NetBSD Foundation, Inc.
5826653c1Skefren * All rights reserved.
6826653c1Skefren *
7826653c1Skefren * This code is derived from software contributed to The NetBSD Foundation
8826653c1Skefren * by Mihai Chelaru <kefren@NetBSD.org>
9826653c1Skefren *
10826653c1Skefren * Redistribution and use in source and binary forms, with or without
11826653c1Skefren * modification, are permitted provided that the following conditions
12826653c1Skefren * are met:
13826653c1Skefren * 1. Redistributions of source code must retain the above copyright
14826653c1Skefren * notice, this list of conditions and the following disclaimer.
15826653c1Skefren * 2. Redistributions in binary form must reproduce the above copyright
16826653c1Skefren * notice, this list of conditions and the following disclaimer in the
17826653c1Skefren * documentation and/or other materials provided with the distribution.
18826653c1Skefren *
19826653c1Skefren * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20826653c1Skefren * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21826653c1Skefren * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22826653c1Skefren * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23826653c1Skefren * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24826653c1Skefren * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25826653c1Skefren * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26826653c1Skefren * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27826653c1Skefren * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28826653c1Skefren * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29826653c1Skefren * POSSIBILITY OF SUCH DAMAGE.
30826653c1Skefren */
31826653c1Skefren
32826653c1Skefren #include <sys/cdefs.h>
33*63f0d21cSthorpej __KERNEL_RCSID(0, "$NetBSD: if_mpls.c,v 1.41 2022/09/03 20:29:31 thorpej Exp $");
34826653c1Skefren
351c4a50f1Spooka #ifdef _KERNEL_OPT
36826653c1Skefren #include "opt_inet.h"
37826653c1Skefren #include "opt_mpls.h"
381c4a50f1Spooka #endif
39826653c1Skefren
40826653c1Skefren #include <sys/param.h>
41826653c1Skefren
42826653c1Skefren #include <sys/errno.h>
43826653c1Skefren #include <sys/malloc.h>
44826653c1Skefren #include <sys/mbuf.h>
45826653c1Skefren #include <sys/sysctl.h>
46826653c1Skefren
47826653c1Skefren #include <net/bpf.h>
48826653c1Skefren #include <net/if.h>
49826653c1Skefren #include <net/if_types.h>
50826653c1Skefren #include <net/route.h>
511d8e08d4Schristos #include <sys/device.h>
521d8e08d4Schristos #include <sys/module.h>
531d8e08d4Schristos #include <sys/atomic.h>
54826653c1Skefren
55826653c1Skefren #ifdef INET
56826653c1Skefren #include <netinet/in.h>
57826653c1Skefren #include <netinet/in_systm.h>
58826653c1Skefren #include <netinet/in_var.h>
59826653c1Skefren #include <netinet/ip.h>
606ea8c2e6Sozaki-r #include <netinet/ip_var.h>
61826653c1Skefren #endif
62826653c1Skefren
63826653c1Skefren #ifdef INET6
64826653c1Skefren #include <netinet/ip6.h>
65826653c1Skefren #include <netinet6/in6_var.h>
66826653c1Skefren #include <netinet6/ip6_var.h>
67826653c1Skefren #endif
68826653c1Skefren
69826653c1Skefren #include <netmpls/mpls.h>
70826653c1Skefren #include <netmpls/mpls_var.h>
71826653c1Skefren
72826653c1Skefren #include "if_mpls.h"
73826653c1Skefren
74e7ae23fdSchristos #include "ioconf.h"
75e7ae23fdSchristos
76826653c1Skefren static int mpls_clone_create(struct if_clone *, int);
77826653c1Skefren static int mpls_clone_destroy(struct ifnet *);
78826653c1Skefren
79826653c1Skefren static struct if_clone mpls_if_cloner =
80826653c1Skefren IF_CLONE_INITIALIZER("mpls", mpls_clone_create, mpls_clone_destroy);
81826653c1Skefren
82826653c1Skefren static void mpls_input(struct ifnet *, struct mbuf *);
83826653c1Skefren static int mpls_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
842cf7873bSozaki-r const struct rtentry *);
85826653c1Skefren static int mpls_ioctl(struct ifnet *, u_long, void *);
862cf7873bSozaki-r static int mpls_send_frame(struct mbuf *, struct ifnet *,
872cf7873bSozaki-r const struct rtentry *);
88826653c1Skefren static int mpls_lse(struct mbuf *);
89826653c1Skefren
90826653c1Skefren #ifdef INET
9155408dcfSmaxv static struct mbuf *mpls_unlabel_inet(struct mbuf *, int *error);
92bd098fd9Skefren static struct mbuf *mpls_label_inet(struct mbuf *, union mpls_shim *, uint);
93826653c1Skefren #endif
94826653c1Skefren
95826653c1Skefren #ifdef INET6
9655408dcfSmaxv static struct mbuf *mpls_unlabel_inet6(struct mbuf *, int *error);
97bd098fd9Skefren static struct mbuf *mpls_label_inet6(struct mbuf *, union mpls_shim *, uint);
98826653c1Skefren #endif
99826653c1Skefren
100826653c1Skefren static struct mbuf *mpls_prepend_shim(struct mbuf *, union mpls_shim *);
101826653c1Skefren
102826653c1Skefren extern int mpls_defttl, mpls_mapttl_inet, mpls_mapttl_inet6, mpls_icmp_respond,
103d27b133dSrtr mpls_forwarding, mpls_frame_accept, mpls_mapprec_inet, mpls_mapclass_inet6,
104326bf6faSkefren mpls_rfc4182;
105826653c1Skefren
1061d8e08d4Schristos static u_int mpls_count;
107*63f0d21cSthorpej
108*63f0d21cSthorpej void mplsattach(int);
109*63f0d21cSthorpej
110826653c1Skefren /* ARGSUSED */
111826653c1Skefren void
mplsattach(int count)1121d8e08d4Schristos mplsattach(int count)
1131d8e08d4Schristos {
1141d8e08d4Schristos /*
1151d8e08d4Schristos * Nothing to do here, initialization is handled by the
1161d8e08d4Schristos * module initialization code in mplsinit() below).
1171d8e08d4Schristos */
1181d8e08d4Schristos }
1191d8e08d4Schristos
1201d8e08d4Schristos static void
mplsinit(void)1211d8e08d4Schristos mplsinit(void)
122826653c1Skefren {
123826653c1Skefren if_clone_attach(&mpls_if_cloner);
124826653c1Skefren }
125826653c1Skefren
126826653c1Skefren static int
mplsdetach(void)1271d8e08d4Schristos mplsdetach(void)
1281d8e08d4Schristos {
1291d8e08d4Schristos int error = 0;
1301d8e08d4Schristos
1311d8e08d4Schristos if (mpls_count != 0)
1321d8e08d4Schristos error = EBUSY;
1331d8e08d4Schristos
1341d8e08d4Schristos if (error == 0)
1351d8e08d4Schristos if_clone_detach(&mpls_if_cloner);
1361d8e08d4Schristos
1371d8e08d4Schristos return error;
1381d8e08d4Schristos }
1391d8e08d4Schristos
1401d8e08d4Schristos static int
mpls_clone_create(struct if_clone * ifc,int unit)141826653c1Skefren mpls_clone_create(struct if_clone *ifc, int unit)
142826653c1Skefren {
143826653c1Skefren struct mpls_softc *sc;
144826653c1Skefren
1451d8e08d4Schristos atomic_inc_uint(&mpls_count);
146826653c1Skefren sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
147826653c1Skefren
148826653c1Skefren if_initname(&sc->sc_if, ifc->ifc_name, unit);
149826653c1Skefren sc->sc_if.if_softc = sc;
150826653c1Skefren sc->sc_if.if_type = IFT_MPLS;
151826653c1Skefren sc->sc_if.if_addrlen = 0;
152826653c1Skefren sc->sc_if.if_hdrlen = sizeof(union mpls_shim);
153826653c1Skefren sc->sc_if.if_dlt = DLT_NULL;
154826653c1Skefren sc->sc_if.if_mtu = 1500;
155826653c1Skefren sc->sc_if.if_flags = 0;
1569c4cd063Sozaki-r sc->sc_if._if_input = mpls_input;
157826653c1Skefren sc->sc_if.if_output = mpls_output;
158826653c1Skefren sc->sc_if.if_ioctl = mpls_ioctl;
159826653c1Skefren
160076e3579Sriastradh if_attach(&sc->sc_if);
161826653c1Skefren if_alloc_sadl(&sc->sc_if);
162826653c1Skefren bpf_attach(&sc->sc_if, DLT_NULL, sizeof(uint32_t));
163826653c1Skefren return 0;
164826653c1Skefren }
165826653c1Skefren
166826653c1Skefren static int
mpls_clone_destroy(struct ifnet * ifp)167826653c1Skefren mpls_clone_destroy(struct ifnet *ifp)
168826653c1Skefren {
169826653c1Skefren
170826653c1Skefren bpf_detach(ifp);
171826653c1Skefren if_detach(ifp);
172826653c1Skefren
173826653c1Skefren free(ifp->if_softc, M_DEVBUF);
1741d8e08d4Schristos atomic_dec_uint(&mpls_count);
175826653c1Skefren return 0;
176826653c1Skefren }
177826653c1Skefren
178826653c1Skefren static void
mpls_input(struct ifnet * ifp,struct mbuf * m)179826653c1Skefren mpls_input(struct ifnet *ifp, struct mbuf *m)
180826653c1Skefren {
181826653c1Skefren #if 0
182826653c1Skefren /*
183826653c1Skefren * TODO - kefren
184826653c1Skefren * I'd love to unshim the packet, guess family
185826653c1Skefren * and pass it to bpf
186826653c1Skefren */
1873cd62456Smsaitoh bpf_mtap_af(ifp, AF_MPLS, m, BPF_D_IN);
188826653c1Skefren #endif
189826653c1Skefren
190826653c1Skefren mpls_lse(m);
191826653c1Skefren }
192826653c1Skefren
193826653c1Skefren void
mplsintr(void * arg __unused)194dd8687ccSthorpej mplsintr(void *arg __unused)
195826653c1Skefren {
1968f4376cbSozaki-r struct mbuf *m;
1978f4376cbSozaki-r
198dd8687ccSthorpej while ((m = pktq_dequeue(mpls_pktq)) != NULL) {
199826653c1Skefren if (((m->m_flags & M_PKTHDR) == 0) ||
200fe6d4275Sozaki-r (m->m_pkthdr.rcvif_index == 0))
201826653c1Skefren panic("mplsintr(): no pkthdr or rcvif");
202826653c1Skefren
203826653c1Skefren #ifdef MBUFTRACE
204826653c1Skefren m_claimm(m, &mpls_owner);
205826653c1Skefren #endif
206fe6d4275Sozaki-r mpls_input(m_get_rcvif_NOMPSAFE(m), m);
207826653c1Skefren }
208826653c1Skefren }
209826653c1Skefren
210826653c1Skefren /*
211826653c1Skefren * prepend shim and deliver
212826653c1Skefren */
213826653c1Skefren static int
mpls_output(struct ifnet * ifp,struct mbuf * m,const struct sockaddr * dst,const struct rtentry * rt)2142cf7873bSozaki-r mpls_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
2152cf7873bSozaki-r const struct rtentry *rt)
216826653c1Skefren {
217bd098fd9Skefren union mpls_shim mh, *pms;
218826653c1Skefren struct rtentry *rt1;
219826653c1Skefren int err;
220bd098fd9Skefren uint psize = sizeof(struct sockaddr_mpls);
221826653c1Skefren
2228a9adb2fSbouyer KASSERT(KERNEL_LOCKED_P());
2238a9adb2fSbouyer
224826653c1Skefren if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
225826653c1Skefren m_freem(m);
226826653c1Skefren return ENETDOWN;
227826653c1Skefren }
228826653c1Skefren
22900914d6dSkefren if (rt_gettag(rt) == NULL || rt_gettag(rt)->sa_family != AF_MPLS) {
230826653c1Skefren m_freem(m);
231826653c1Skefren return EINVAL;
232826653c1Skefren }
233826653c1Skefren
2343cd62456Smsaitoh bpf_mtap_af(ifp, dst->sa_family, m, BPF_D_OUT);
235826653c1Skefren
236bd098fd9Skefren memset(&mh, 0, sizeof(mh));
237826653c1Skefren mh.s_addr = MPLS_GETSADDR(rt);
238826653c1Skefren mh.shim.bos = 1;
239826653c1Skefren mh.shim.exp = 0;
240826653c1Skefren mh.shim.ttl = mpls_defttl;
241826653c1Skefren
242bd098fd9Skefren pms = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr;
243bd098fd9Skefren
244bd098fd9Skefren while (psize <= rt_gettag(rt)->sa_len - sizeof(mh)) {
245bd098fd9Skefren pms++;
2463837722cSkefren if (mh.shim.label != MPLS_LABEL_IMPLNULL &&
2473837722cSkefren ((m = mpls_prepend_shim(m, &mh)) == NULL))
248561daf4fSkefren return ENOBUFS;
249bd098fd9Skefren memset(&mh, 0, sizeof(mh));
250bd098fd9Skefren mh.s_addr = ntohl(pms->s_addr);
251bd098fd9Skefren mh.shim.bos = mh.shim.exp = 0;
252bd098fd9Skefren mh.shim.ttl = mpls_defttl;
253bd098fd9Skefren psize += sizeof(mh);
254bd098fd9Skefren }
255bd098fd9Skefren
256826653c1Skefren switch (dst->sa_family) {
257826653c1Skefren #ifdef INET
258826653c1Skefren case AF_INET:
259bd098fd9Skefren m = mpls_label_inet(m, &mh, psize - sizeof(struct sockaddr_mpls));
260826653c1Skefren break;
261826653c1Skefren #endif
262826653c1Skefren #ifdef INET6
263826653c1Skefren case AF_INET6:
264bd098fd9Skefren m = mpls_label_inet6(m, &mh, psize - sizeof(struct sockaddr_mpls));
265826653c1Skefren break;
266826653c1Skefren #endif
267826653c1Skefren default:
268826653c1Skefren m = mpls_prepend_shim(m, &mh);
269826653c1Skefren break;
270826653c1Skefren }
271826653c1Skefren
272826653c1Skefren if (m == NULL) {
273826653c1Skefren IF_DROP(&ifp->if_snd);
27470b554e6Sthorpej if_statinc(ifp, if_oerrors);
275826653c1Skefren return ENOBUFS;
276826653c1Skefren }
277826653c1Skefren
27870b554e6Sthorpej if_statadd2(ifp, if_opackets, 1, if_obytes, m->m_pkthdr.len);
279826653c1Skefren
280826653c1Skefren if ((rt1 = rtalloc1(rt->rt_gateway, 1)) == NULL) {
281826653c1Skefren m_freem(m);
282826653c1Skefren return EHOSTUNREACH;
283826653c1Skefren }
284826653c1Skefren
285826653c1Skefren err = mpls_send_frame(m, rt1->rt_ifp, rt);
2866fb88806Sozaki-r rt_unref(rt1);
287826653c1Skefren return err;
288826653c1Skefren }
289826653c1Skefren
290826653c1Skefren static int
mpls_ioctl(struct ifnet * ifp,u_long cmd,void * data)291826653c1Skefren mpls_ioctl(struct ifnet *ifp, u_long cmd, void *data)
292826653c1Skefren {
293826653c1Skefren int error = 0, s = splnet();
294826653c1Skefren struct ifreq *ifr = data;
295826653c1Skefren
296826653c1Skefren switch(cmd) {
297826653c1Skefren case SIOCINITIFADDR:
298826653c1Skefren ifp->if_flags |= IFF_UP | IFF_RUNNING;
299826653c1Skefren break;
300826653c1Skefren case SIOCSIFMTU:
301826653c1Skefren if (ifr != NULL && ifr->ifr_mtu < 576) {
302826653c1Skefren error = EINVAL;
303826653c1Skefren break;
304826653c1Skefren }
305826653c1Skefren /* FALLTHROUGH */
306826653c1Skefren case SIOCGIFMTU:
307826653c1Skefren if ((error = ifioctl_common(ifp, cmd, data)) == ENETRESET)
308826653c1Skefren error = 0;
309826653c1Skefren break;
310826653c1Skefren case SIOCSIFFLAGS:
311826653c1Skefren if ((error = ifioctl_common(ifp, cmd, data)) != 0)
312826653c1Skefren break;
313826653c1Skefren if (ifp->if_flags & IFF_UP)
314826653c1Skefren ifp->if_flags |= IFF_RUNNING;
315826653c1Skefren break;
316826653c1Skefren default:
317826653c1Skefren error = ifioctl_common(ifp, cmd, data);
318826653c1Skefren break;
319826653c1Skefren }
320826653c1Skefren splx(s);
321826653c1Skefren return error;
322826653c1Skefren }
323826653c1Skefren
3245eeded08Smaxv static inline struct mbuf *
mpls_trim_label(struct mbuf * m,union mpls_shim * sh)3255eeded08Smaxv mpls_trim_label(struct mbuf *m, union mpls_shim *sh)
3265eeded08Smaxv {
3275eeded08Smaxv m_adj(m, sizeof(union mpls_shim));
3285eeded08Smaxv
3295eeded08Smaxv if (m->m_len < sizeof(union mpls_shim) &&
3305eeded08Smaxv (m = m_pullup(m, sizeof(union mpls_shim))) == NULL)
3315eeded08Smaxv return NULL;
3325eeded08Smaxv
3335eeded08Smaxv sh->s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
3345eeded08Smaxv
3355eeded08Smaxv return m;
3365eeded08Smaxv }
3375eeded08Smaxv
338826653c1Skefren /*
339826653c1Skefren * MPLS Label Switch Engine
340826653c1Skefren */
341826653c1Skefren static int
mpls_lse(struct mbuf * m)342826653c1Skefren mpls_lse(struct mbuf *m)
343826653c1Skefren {
344826653c1Skefren struct sockaddr_mpls dst;
345826653c1Skefren union mpls_shim tshim, *htag;
346826653c1Skefren struct rtentry *rt = NULL;
347826653c1Skefren int error = ENOBUFS;
348561daf4fSkefren uint psize = sizeof(struct sockaddr_mpls);
349829b00a6Skefren bool push_back_alert = false;
350826653c1Skefren
351ac4e3a2eSmaxv /* If we're not accepting MPLS frames, leave now. */
352ac4e3a2eSmaxv if (!mpls_frame_accept) {
353ac4e3a2eSmaxv error = EINVAL;
354ac4e3a2eSmaxv goto done;
355ac4e3a2eSmaxv }
356ac4e3a2eSmaxv
357826653c1Skefren if (m->m_len < sizeof(union mpls_shim) &&
358826653c1Skefren (m = m_pullup(m, sizeof(union mpls_shim))) == NULL)
359826653c1Skefren goto done;
360826653c1Skefren
361826653c1Skefren dst.smpls_len = sizeof(struct sockaddr_mpls);
362826653c1Skefren dst.smpls_family = AF_MPLS;
363826653c1Skefren dst.smpls_addr.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
364826653c1Skefren
365826653c1Skefren error = EINVAL;
366826653c1Skefren
367826653c1Skefren /* TTL decrement */
368826653c1Skefren if ((m = mpls_ttl_dec(m)) == NULL)
369826653c1Skefren goto done;
370826653c1Skefren
371326bf6faSkefren /* RFC 4182 */
37255408dcfSmaxv if (mpls_rfc4182 != 0) {
373326bf6faSkefren while ((dst.smpls_addr.shim.label == MPLS_LABEL_IPV4NULL ||
374326bf6faSkefren dst.smpls_addr.shim.label == MPLS_LABEL_IPV6NULL) &&
3755eeded08Smaxv __predict_false(dst.smpls_addr.shim.bos == 0)) {
3765eeded08Smaxv m = mpls_trim_label(m, &dst.smpls_addr);
3775eeded08Smaxv if (m == NULL) {
3785eeded08Smaxv goto done;
3795eeded08Smaxv }
3805eeded08Smaxv }
38155408dcfSmaxv }
382829b00a6Skefren
383829b00a6Skefren /* RFC 3032 Section 2.1 Page 4 */
384829b00a6Skefren if (__predict_false(dst.smpls_addr.shim.label == MPLS_LABEL_RTALERT) &&
385829b00a6Skefren dst.smpls_addr.shim.bos == 0) {
3865eeded08Smaxv m = mpls_trim_label(m, &dst.smpls_addr);
3875eeded08Smaxv if (m == NULL) {
3885eeded08Smaxv goto done;
3895eeded08Smaxv }
390829b00a6Skefren push_back_alert = true;
391326bf6faSkefren }
392326bf6faSkefren
393826653c1Skefren if (dst.smpls_addr.shim.label <= MPLS_LABEL_RESMAX) {
394826653c1Skefren /* Don't swap reserved labels */
395826653c1Skefren switch (dst.smpls_addr.shim.label) {
396826653c1Skefren #ifdef INET
397826653c1Skefren case MPLS_LABEL_IPV4NULL:
398826653c1Skefren /* Pop shim and push mbuf to IP stack */
39955408dcfSmaxv if (dst.smpls_addr.shim.bos) {
40055408dcfSmaxv m = mpls_unlabel_inet(m, &error);
40155408dcfSmaxv }
402826653c1Skefren break;
403826653c1Skefren #endif
404826653c1Skefren #ifdef INET6
405826653c1Skefren case MPLS_LABEL_IPV6NULL:
406826653c1Skefren /* Pop shim and push mbuf to IPv6 stack */
40755408dcfSmaxv if (dst.smpls_addr.shim.bos) {
40855408dcfSmaxv m = mpls_unlabel_inet6(m, &error);
40955408dcfSmaxv }
410826653c1Skefren break;
411826653c1Skefren #endif
412826653c1Skefren case MPLS_LABEL_RTALERT: /* Yeah, I'm all alerted */
413826653c1Skefren case MPLS_LABEL_IMPLNULL: /* This is logical only */
414826653c1Skefren default: /* Rest are not allowed */
415826653c1Skefren break;
416826653c1Skefren }
417826653c1Skefren goto done;
418826653c1Skefren }
419826653c1Skefren
420826653c1Skefren /* Check if we should do MPLS forwarding */
421826653c1Skefren error = EHOSTUNREACH;
422826653c1Skefren if (!mpls_forwarding)
423826653c1Skefren goto done;
424826653c1Skefren
425826653c1Skefren /* Get a route to dst */
4265eeded08Smaxv dst.smpls_addr.shim.ttl = 0;
4275eeded08Smaxv dst.smpls_addr.shim.bos = 0;
428826653c1Skefren dst.smpls_addr.shim.exp = 0;
429826653c1Skefren dst.smpls_addr.s_addr = htonl(dst.smpls_addr.s_addr);
430826653c1Skefren if ((rt = rtalloc1((const struct sockaddr*)&dst, 1)) == NULL)
431826653c1Skefren goto done;
432826653c1Skefren
43300914d6dSkefren /* MPLS packet with no MPLS tagged route ? */
434826653c1Skefren if ((rt->rt_flags & RTF_GATEWAY) == 0 ||
43500914d6dSkefren rt_gettag(rt) == NULL ||
43600914d6dSkefren rt_gettag(rt)->sa_family != AF_MPLS)
437826653c1Skefren goto done;
438826653c1Skefren
439826653c1Skefren tshim.s_addr = MPLS_GETSADDR(rt);
440826653c1Skefren
441826653c1Skefren /* Swap labels */
442826653c1Skefren if ((m->m_len < sizeof(union mpls_shim)) &&
443826653c1Skefren (m = m_pullup(m, sizeof(union mpls_shim))) == 0) {
444826653c1Skefren error = ENOBUFS;
445826653c1Skefren goto done;
446826653c1Skefren }
447826653c1Skefren
448826653c1Skefren /* Replace only the label */
449826653c1Skefren htag = mtod(m, union mpls_shim *);
450826653c1Skefren htag->s_addr = ntohl(htag->s_addr);
451826653c1Skefren htag->shim.label = tshim.shim.label;
452826653c1Skefren htag->s_addr = htonl(htag->s_addr);
453826653c1Skefren
454561daf4fSkefren /* check if there is anything more to prepend */
455561daf4fSkefren htag = &((struct sockaddr_mpls*)rt_gettag(rt))->smpls_addr;
456561daf4fSkefren while (psize <= rt_gettag(rt)->sa_len - sizeof(tshim)) {
457561daf4fSkefren htag++;
458561daf4fSkefren memset(&tshim, 0, sizeof(tshim));
459561daf4fSkefren tshim.s_addr = ntohl(htag->s_addr);
460561daf4fSkefren tshim.shim.bos = tshim.shim.exp = 0;
461561daf4fSkefren tshim.shim.ttl = mpls_defttl;
4623837722cSkefren if (tshim.shim.label != MPLS_LABEL_IMPLNULL &&
46355408dcfSmaxv ((m = mpls_prepend_shim(m, &tshim)) == NULL)) {
46455408dcfSmaxv error = ENOBUFS;
46555408dcfSmaxv goto done;
46655408dcfSmaxv }
467561daf4fSkefren psize += sizeof(tshim);
468561daf4fSkefren }
469561daf4fSkefren
470829b00a6Skefren if (__predict_false(push_back_alert == true)) {
471829b00a6Skefren /* re-add the router alert label */
472829b00a6Skefren memset(&tshim, 0, sizeof(tshim));
473829b00a6Skefren tshim.s_addr = MPLS_LABEL_RTALERT;
474829b00a6Skefren tshim.shim.bos = tshim.shim.exp = 0;
475829b00a6Skefren tshim.shim.ttl = mpls_defttl;
47655408dcfSmaxv if ((m = mpls_prepend_shim(m, &tshim)) == NULL) {
47755408dcfSmaxv error = ENOBUFS;
47855408dcfSmaxv goto done;
47955408dcfSmaxv }
480829b00a6Skefren }
481829b00a6Skefren
4822cf7873bSozaki-r if ((rt->rt_flags & RTF_GATEWAY) == 0) {
4832cf7873bSozaki-r error = EHOSTUNREACH;
4842cf7873bSozaki-r goto done;
4852cf7873bSozaki-r }
4862cf7873bSozaki-r
4872cf7873bSozaki-r rt->rt_use++;
488826653c1Skefren error = mpls_send_frame(m, rt->rt_ifp, rt);
489826653c1Skefren
490826653c1Skefren done:
491826653c1Skefren if (error != 0 && m != NULL)
492826653c1Skefren m_freem(m);
493826653c1Skefren if (rt != NULL)
4946fb88806Sozaki-r rt_unref(rt);
495826653c1Skefren
496826653c1Skefren return error;
497826653c1Skefren }
498826653c1Skefren
499826653c1Skefren static int
mpls_send_frame(struct mbuf * m,struct ifnet * ifp,const struct rtentry * rt)5002cf7873bSozaki-r mpls_send_frame(struct mbuf *m, struct ifnet *ifp, const struct rtentry *rt)
501826653c1Skefren {
502826653c1Skefren union mpls_shim msh;
5038a9adb2fSbouyer int ret;
504826653c1Skefren
505826653c1Skefren msh.s_addr = MPLS_GETSADDR(rt);
50687fd7aebSkefren if (msh.shim.label == MPLS_LABEL_IMPLNULL ||
50787fd7aebSkefren (m->m_flags & (M_MCAST | M_BCAST))) {
508826653c1Skefren m_adj(m, sizeof(union mpls_shim));
509826653c1Skefren m->m_pkthdr.csum_flags = 0;
510826653c1Skefren }
511826653c1Skefren
512826653c1Skefren switch(ifp->if_type) {
513a91123ebSkefren /* only these are supported for now */
514826653c1Skefren case IFT_ETHER:
515826653c1Skefren case IFT_TUNNEL:
516826653c1Skefren case IFT_LOOP:
5176ea8c2e6Sozaki-r #ifdef INET
5189e0f6c5eSozaki-r ret = ip_if_output(ifp, m, rt->rt_gateway, rt);
5196ea8c2e6Sozaki-r #else
52010b439dfSknakahara ret = if_output_lock(ifp, ifp, m, rt->rt_gateway, rt);
5216ea8c2e6Sozaki-r #endif
5228a9adb2fSbouyer return ret;
523826653c1Skefren break;
524826653c1Skefren default:
525826653c1Skefren return ENETUNREACH;
526826653c1Skefren }
527826653c1Skefren return 0;
528826653c1Skefren }
529826653c1Skefren
530826653c1Skefren #ifdef INET
53155408dcfSmaxv static struct mbuf *
mpls_unlabel_inet(struct mbuf * m,int * error)53255408dcfSmaxv mpls_unlabel_inet(struct mbuf *m, int *error)
533826653c1Skefren {
534826653c1Skefren struct ip *iph;
5355eeded08Smaxv union mpls_shim ms;
53660d350cfSrmind int iphlen;
537826653c1Skefren
538826653c1Skefren if (mpls_mapttl_inet || mpls_mapprec_inet) {
539826653c1Skefren /* get shim info */
5405eeded08Smaxv ms.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
541826653c1Skefren
542826653c1Skefren /* and get rid of it */
543826653c1Skefren m_adj(m, sizeof(union mpls_shim));
544826653c1Skefren
545826653c1Skefren /* get ip header */
546826653c1Skefren if (m->m_len < sizeof(struct ip) &&
54755408dcfSmaxv (m = m_pullup(m, sizeof(struct ip))) == NULL) {
54855408dcfSmaxv *error = ENOBUFS;
54955408dcfSmaxv return NULL;
55055408dcfSmaxv }
55155408dcfSmaxv
552826653c1Skefren iph = mtod(m, struct ip *);
553826653c1Skefren iphlen = iph->ip_hl << 2;
554826653c1Skefren
555826653c1Skefren /* get it all */
556826653c1Skefren if (m->m_len < iphlen) {
55755408dcfSmaxv if ((m = m_pullup(m, iphlen)) == NULL) {
55855408dcfSmaxv *error = ENOBUFS;
55955408dcfSmaxv return NULL;
56055408dcfSmaxv }
561826653c1Skefren iph = mtod(m, struct ip *);
562826653c1Skefren }
563826653c1Skefren
564826653c1Skefren /* check ipsum */
565826653c1Skefren if (in_cksum(m, iphlen) != 0) {
566826653c1Skefren m_freem(m);
56755408dcfSmaxv *error = EINVAL;
56855408dcfSmaxv return NULL;
569826653c1Skefren }
570826653c1Skefren
571826653c1Skefren /* set IP ttl from MPLS ttl */
572826653c1Skefren if (mpls_mapttl_inet)
5735eeded08Smaxv iph->ip_ttl = ms.shim.ttl;
574826653c1Skefren
575826653c1Skefren /* set IP Precedence from MPLS Exp */
576826653c1Skefren if (mpls_mapprec_inet) {
577826653c1Skefren iph->ip_tos = (iph->ip_tos << 3) >> 3;
5785eeded08Smaxv iph->ip_tos |= ms.shim.exp << 5;
579826653c1Skefren }
580826653c1Skefren
581826653c1Skefren /* reset ipsum because we modified TTL and TOS */
582826653c1Skefren iph->ip_sum = 0;
583826653c1Skefren iph->ip_sum = in_cksum(m, iphlen);
58455408dcfSmaxv } else {
585826653c1Skefren m_adj(m, sizeof(union mpls_shim));
58655408dcfSmaxv }
587826653c1Skefren
588826653c1Skefren /* Put it on IP queue */
58960d350cfSrmind if (__predict_false(!pktq_enqueue(ip_pktq, m, 0))) {
590826653c1Skefren m_freem(m);
59155408dcfSmaxv *error = ENOBUFS;
59255408dcfSmaxv return NULL;
593826653c1Skefren }
59455408dcfSmaxv
59555408dcfSmaxv *error = 0;
59655408dcfSmaxv return m;
597826653c1Skefren }
598826653c1Skefren
599826653c1Skefren /*
600826653c1Skefren * Prepend MPLS label
601826653c1Skefren */
602826653c1Skefren static struct mbuf *
mpls_label_inet(struct mbuf * m,union mpls_shim * ms,uint offset)603bd098fd9Skefren mpls_label_inet(struct mbuf *m, union mpls_shim *ms, uint offset)
604826653c1Skefren {
605931515b1Skefren struct ip iphdr;
606826653c1Skefren
607826653c1Skefren if (mpls_mapttl_inet || mpls_mapprec_inet) {
6085eeded08Smaxv /* XXX Maybe just check m->m_pkthdr.len instead? */
6095eeded08Smaxv if ((m->m_len < offset + sizeof(struct ip)) &&
610bd098fd9Skefren (m = m_pullup(m, offset + sizeof(struct ip))) == 0)
6115eeded08Smaxv return NULL;
6125eeded08Smaxv
613931515b1Skefren m_copydata(m, offset, sizeof(struct ip), &iphdr);
614826653c1Skefren
615826653c1Skefren /* Map TTL */
616826653c1Skefren if (mpls_mapttl_inet)
617931515b1Skefren ms->shim.ttl = iphdr.ip_ttl;
618826653c1Skefren
619826653c1Skefren /* Copy IP precedence to EXP */
620826653c1Skefren if (mpls_mapprec_inet)
621931515b1Skefren ms->shim.exp = ((u_int8_t)iphdr.ip_tos) >> 5;
622826653c1Skefren }
623826653c1Skefren
624826653c1Skefren if ((m = mpls_prepend_shim(m, ms)) == NULL)
625826653c1Skefren return NULL;
626826653c1Skefren
627826653c1Skefren return m;
628826653c1Skefren }
629826653c1Skefren #endif /* INET */
630826653c1Skefren
631826653c1Skefren #ifdef INET6
63255408dcfSmaxv static struct mbuf *
mpls_unlabel_inet6(struct mbuf * m,int * error)63355408dcfSmaxv mpls_unlabel_inet6(struct mbuf *m, int *error)
634826653c1Skefren {
635826653c1Skefren struct ip6_hdr *ip6hdr;
636826653c1Skefren union mpls_shim ms;
637826653c1Skefren
638826653c1Skefren /* TODO: mapclass */
639826653c1Skefren if (mpls_mapttl_inet6) {
640826653c1Skefren ms.s_addr = ntohl(mtod(m, union mpls_shim *)->s_addr);
641826653c1Skefren m_adj(m, sizeof(union mpls_shim));
642826653c1Skefren
643826653c1Skefren if (m->m_len < sizeof(struct ip6_hdr) &&
64455408dcfSmaxv (m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) {
64555408dcfSmaxv *error = ENOBUFS;
64655408dcfSmaxv return NULL;
64755408dcfSmaxv }
648826653c1Skefren ip6hdr = mtod(m, struct ip6_hdr *);
649826653c1Skefren
650826653c1Skefren /* Because we just decremented this in mpls_lse */
651826653c1Skefren ip6hdr->ip6_hlim = ms.shim.ttl + 1;
65255408dcfSmaxv } else {
653826653c1Skefren m_adj(m, sizeof(union mpls_shim));
65455408dcfSmaxv }
655826653c1Skefren
65660d350cfSrmind /* Put it back on IPv6 queue. */
65760d350cfSrmind if (__predict_false(!pktq_enqueue(ip6_pktq, m, 0))) {
658826653c1Skefren m_freem(m);
65955408dcfSmaxv *error = ENOBUFS;
66055408dcfSmaxv return NULL;
661826653c1Skefren }
66255408dcfSmaxv
66355408dcfSmaxv *error = 0;
66455408dcfSmaxv return m;
665826653c1Skefren }
666826653c1Skefren
667826653c1Skefren static struct mbuf *
mpls_label_inet6(struct mbuf * m,union mpls_shim * ms,uint offset)668bd098fd9Skefren mpls_label_inet6(struct mbuf *m, union mpls_shim *ms, uint offset)
669826653c1Skefren {
670931515b1Skefren struct ip6_hdr ip6h;
671826653c1Skefren
672826653c1Skefren if (mpls_mapttl_inet6 || mpls_mapclass_inet6) {
6735eeded08Smaxv /* XXX Maybe just check m->m_pkthdr.len instead? */
6745eeded08Smaxv if ((m->m_len < offset + sizeof(struct ip6_hdr)) &&
675bd098fd9Skefren (m = m_pullup(m, offset + sizeof(struct ip6_hdr))) == 0)
676826653c1Skefren return NULL;
6775eeded08Smaxv
678931515b1Skefren m_copydata(m, offset, sizeof(struct ip6_hdr), &ip6h);
679826653c1Skefren
680826653c1Skefren if (mpls_mapttl_inet6)
681931515b1Skefren ms->shim.ttl = ip6h.ip6_hlim;
682826653c1Skefren
683826653c1Skefren if (mpls_mapclass_inet6)
684931515b1Skefren ms->shim.exp = ip6h.ip6_vfc << 1 >> 5;
685826653c1Skefren }
686826653c1Skefren
687826653c1Skefren if ((m = mpls_prepend_shim(m, ms)) == NULL)
688826653c1Skefren return NULL;
689826653c1Skefren
690826653c1Skefren return m;
691826653c1Skefren }
692826653c1Skefren #endif /* INET6 */
693826653c1Skefren
694826653c1Skefren static struct mbuf *
mpls_prepend_shim(struct mbuf * m,union mpls_shim * ms)695826653c1Skefren mpls_prepend_shim(struct mbuf *m, union mpls_shim *ms)
696826653c1Skefren {
697826653c1Skefren union mpls_shim *shim;
698826653c1Skefren
699826653c1Skefren M_PREPEND(m, sizeof(*ms), M_DONTWAIT);
700826653c1Skefren if (m == NULL)
701826653c1Skefren return NULL;
702826653c1Skefren
703826653c1Skefren if (m->m_len < sizeof(union mpls_shim) &&
704826653c1Skefren (m = m_pullup(m, sizeof(union mpls_shim))) == 0)
705826653c1Skefren return NULL;
706826653c1Skefren
707826653c1Skefren shim = mtod(m, union mpls_shim *);
708826653c1Skefren
709826653c1Skefren memcpy(shim, ms, sizeof(*shim));
710826653c1Skefren shim->s_addr = htonl(shim->s_addr);
711826653c1Skefren
712826653c1Skefren return m;
713826653c1Skefren }
7141d8e08d4Schristos
7151d8e08d4Schristos /*
7161d8e08d4Schristos * Module infrastructure
7171d8e08d4Schristos */
7181d8e08d4Schristos #include "if_module.h"
7191d8e08d4Schristos
720befc8be5Spgoyette IF_MODULE(MODULE_CLASS_DRIVER, mpls, NULL)
721