Line data Source code
1 : /* $OpenBSD: if_mpw.c,v 1.24 2018/02/19 08:59:52 mpi 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_softc {
48 : struct ifnet sc_if;
49 :
50 : struct ifaddr sc_ifa;
51 : struct sockaddr_mpls sc_smpls; /* Local label */
52 :
53 : uint32_t sc_flags;
54 : uint32_t sc_type;
55 : struct shim_hdr sc_rshim;
56 : struct sockaddr_storage sc_nexthop;
57 : };
58 :
59 : void mpwattach(int);
60 : int mpw_clone_create(struct if_clone *, int);
61 : int mpw_clone_destroy(struct ifnet *);
62 : int mpw_ioctl(struct ifnet *, u_long, caddr_t);
63 : int mpw_output(struct ifnet *, struct mbuf *, struct sockaddr *,
64 : struct rtentry *);
65 : void mpw_start(struct ifnet *);
66 : int mpw_input(struct ifnet *, struct mbuf *, void *);
67 : #if NVLAN > 0
68 : struct mbuf *mpw_vlan_handle(struct mbuf *, struct mpw_softc *);
69 : #endif /* NVLAN */
70 :
71 : struct if_clone mpw_cloner =
72 : IF_CLONE_INITIALIZER("mpw", mpw_clone_create, mpw_clone_destroy);
73 :
74 : void
75 0 : mpwattach(int n)
76 : {
77 0 : if_clone_attach(&mpw_cloner);
78 0 : }
79 :
80 : int
81 0 : mpw_clone_create(struct if_clone *ifc, int unit)
82 : {
83 : struct mpw_softc *sc;
84 : struct ifnet *ifp;
85 :
86 0 : sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO);
87 0 : ifp = &sc->sc_if;
88 0 : snprintf(ifp->if_xname, sizeof(ifp->if_xname), "mpw%d", unit);
89 0 : ifp->if_softc = sc;
90 0 : ifp->if_mtu = ETHERMTU;
91 0 : ifp->if_flags = IFF_POINTOPOINT;
92 0 : ifp->if_xflags = IFXF_CLONED;
93 0 : ifp->if_ioctl = mpw_ioctl;
94 0 : ifp->if_output = mpw_output;
95 0 : ifp->if_start = mpw_start;
96 0 : ifp->if_type = IFT_MPLSTUNNEL;
97 0 : ifp->if_hdrlen = ETHER_HDR_LEN;
98 0 : IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
99 :
100 0 : if_attach(ifp);
101 0 : if_alloc_sadl(ifp);
102 :
103 0 : sc->sc_ifa.ifa_ifp = ifp;
104 0 : sc->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
105 0 : sc->sc_smpls.smpls_len = sizeof(sc->sc_smpls);
106 0 : sc->sc_smpls.smpls_family = AF_MPLS;
107 :
108 0 : if_ih_insert(ifp, mpw_input, NULL);
109 :
110 : #if NBPFILTER > 0
111 0 : bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, ETHER_HDR_LEN);
112 : #endif /* NBFILTER */
113 :
114 0 : return (0);
115 : }
116 :
117 : int
118 0 : mpw_clone_destroy(struct ifnet *ifp)
119 : {
120 0 : struct mpw_softc *sc = ifp->if_softc;
121 :
122 0 : ifp->if_flags &= ~IFF_RUNNING;
123 :
124 0 : if (sc->sc_smpls.smpls_label) {
125 0 : rt_ifa_del(&sc->sc_ifa, RTF_MPLS,
126 0 : smplstosa(&sc->sc_smpls));
127 0 : }
128 :
129 0 : if_ih_remove(ifp, mpw_input, NULL);
130 :
131 0 : if_detach(ifp);
132 0 : free(sc, M_DEVBUF, sizeof(*sc));
133 :
134 0 : return (0);
135 : }
136 :
137 : int
138 0 : mpw_input(struct ifnet *ifp, struct mbuf *m, void *cookie)
139 : {
140 : /* Don't have local broadcast. */
141 0 : m_freem(m);
142 0 : return (1);
143 : }
144 :
145 : int
146 0 : mpw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
147 : {
148 0 : struct ifreq *ifr = (struct ifreq *) data;
149 0 : struct mpw_softc *sc = ifp->if_softc;
150 : struct sockaddr_in *sin;
151 : struct sockaddr_in *sin_nexthop;
152 : int error = 0;
153 0 : struct ifmpwreq imr;
154 :
155 0 : switch (cmd) {
156 : case SIOCSIFMTU:
157 0 : if (ifr->ifr_mtu < MPE_MTU_MIN ||
158 0 : ifr->ifr_mtu > MPE_MTU_MAX)
159 0 : error = EINVAL;
160 : else
161 0 : ifp->if_mtu = ifr->ifr_mtu;
162 : break;
163 :
164 : case SIOCSIFFLAGS:
165 0 : if ((ifp->if_flags & IFF_UP))
166 0 : ifp->if_flags |= IFF_RUNNING;
167 : else
168 0 : ifp->if_flags &= ~IFF_RUNNING;
169 : break;
170 :
171 : case SIOCSETMPWCFG:
172 0 : error = suser(curproc);
173 0 : if (error != 0)
174 : break;
175 :
176 0 : error = copyin(ifr->ifr_data, &imr, sizeof(imr));
177 0 : if (error != 0)
178 : break;
179 :
180 : /* Teardown all configuration if got no nexthop */
181 0 : sin = (struct sockaddr_in *) &imr.imr_nexthop;
182 0 : if (sin->sin_addr.s_addr == 0) {
183 0 : if (rt_ifa_del(&sc->sc_ifa, RTF_MPLS,
184 0 : smplstosa(&sc->sc_smpls)) == 0)
185 0 : sc->sc_smpls.smpls_label = 0;
186 :
187 0 : memset(&sc->sc_rshim, 0, sizeof(sc->sc_rshim));
188 0 : memset(&sc->sc_nexthop, 0, sizeof(sc->sc_nexthop));
189 0 : sc->sc_flags = 0;
190 0 : sc->sc_type = 0;
191 0 : break;
192 : }
193 :
194 : /* Validate input */
195 0 : if (sin->sin_family != AF_INET ||
196 0 : imr.imr_lshim.shim_label > MPLS_LABEL_MAX ||
197 0 : imr.imr_lshim.shim_label <= MPLS_LABEL_RESERVED_MAX ||
198 0 : imr.imr_rshim.shim_label > MPLS_LABEL_MAX ||
199 0 : imr.imr_rshim.shim_label <= MPLS_LABEL_RESERVED_MAX) {
200 : error = EINVAL;
201 0 : break;
202 : }
203 :
204 : /* Setup labels and create inbound route */
205 0 : imr.imr_lshim.shim_label =
206 0 : htonl(imr.imr_lshim.shim_label << MPLS_LABEL_OFFSET);
207 0 : imr.imr_rshim.shim_label =
208 0 : htonl(imr.imr_rshim.shim_label << MPLS_LABEL_OFFSET);
209 :
210 0 : if (sc->sc_smpls.smpls_label != imr.imr_lshim.shim_label) {
211 0 : if (sc->sc_smpls.smpls_label)
212 0 : rt_ifa_del(&sc->sc_ifa, RTF_MPLS,
213 0 : smplstosa(&sc->sc_smpls));
214 :
215 0 : sc->sc_smpls.smpls_label = imr.imr_lshim.shim_label;
216 0 : error = rt_ifa_add(&sc->sc_ifa, RTF_MPLS,
217 0 : smplstosa(&sc->sc_smpls));
218 0 : if (error != 0) {
219 0 : sc->sc_smpls.smpls_label = 0;
220 0 : break;
221 : }
222 : }
223 :
224 : /* Apply configuration */
225 0 : sc->sc_flags = imr.imr_flags;
226 0 : sc->sc_type = imr.imr_type;
227 0 : sc->sc_rshim.shim_label = imr.imr_rshim.shim_label;
228 0 : sc->sc_rshim.shim_label |= MPLS_BOS_MASK;
229 :
230 0 : memset(&sc->sc_nexthop, 0, sizeof(sc->sc_nexthop));
231 0 : sin_nexthop = (struct sockaddr_in *) &sc->sc_nexthop;
232 0 : sin_nexthop->sin_family = sin->sin_family;
233 0 : sin_nexthop->sin_len = sizeof(struct sockaddr_in);
234 0 : sin_nexthop->sin_addr.s_addr = sin->sin_addr.s_addr;
235 0 : break;
236 :
237 : case SIOCGETMPWCFG:
238 0 : imr.imr_flags = sc->sc_flags;
239 0 : imr.imr_type = sc->sc_type;
240 0 : imr.imr_lshim.shim_label =
241 0 : ((ntohl(sc->sc_smpls.smpls_label & MPLS_LABEL_MASK)) >>
242 : MPLS_LABEL_OFFSET);
243 0 : imr.imr_rshim.shim_label =
244 0 : ((ntohl(sc->sc_rshim.shim_label & MPLS_LABEL_MASK)) >>
245 : MPLS_LABEL_OFFSET);
246 0 : memcpy(&imr.imr_nexthop, &sc->sc_nexthop,
247 : sizeof(imr.imr_nexthop));
248 :
249 0 : error = copyout(&imr, ifr->ifr_data, sizeof(imr));
250 0 : break;
251 :
252 : default:
253 : error = ENOTTY;
254 0 : break;
255 : }
256 :
257 0 : return (error);
258 0 : }
259 :
260 : int
261 0 : mpw_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
262 : struct rtentry *rt)
263 : {
264 0 : struct mpw_softc *sc = ifp->if_softc;
265 0 : struct mbuf_list ml = MBUF_LIST_INITIALIZER();
266 0 : struct ether_header *eh, ehc;
267 : struct shim_hdr *shim;
268 :
269 0 : if (sc->sc_type == IMR_TYPE_NONE) {
270 0 : m_freem(m);
271 0 : return (EHOSTUNREACH);
272 : }
273 :
274 0 : if (sc->sc_flags & IMR_FLAG_CONTROLWORD) {
275 0 : shim = mtod(m, struct shim_hdr *);
276 0 : m_adj(m, MPLS_HDRLEN);
277 :
278 : /*
279 : * The first 4 bits identifies that this packet is a
280 : * control word. If the control word is configured and
281 : * we received an IP datagram we shall drop it.
282 : */
283 0 : if (shim->shim_label & CW_ZERO_MASK) {
284 0 : ifp->if_ierrors++;
285 0 : m_freem(m);
286 0 : return (EINVAL);
287 : }
288 :
289 : /* We don't support fragmentation just yet. */
290 0 : if (shim->shim_label & CW_FRAG_MASK) {
291 0 : ifp->if_ierrors++;
292 0 : m_freem(m);
293 0 : return (EINVAL);
294 : }
295 : }
296 :
297 0 : if (sc->sc_type == IMR_TYPE_ETHERNET_TAGGED) {
298 0 : m_copydata(m, 0, sizeof(ehc), (caddr_t) &ehc);
299 0 : m_adj(m, ETHER_HDR_LEN);
300 :
301 : /* Ethernet tagged expects at least 2 VLANs */
302 0 : if (ntohs(ehc.ether_type) != ETHERTYPE_QINQ) {
303 0 : ifp->if_ierrors++;
304 0 : m_freem(m);
305 0 : return (EINVAL);
306 : }
307 :
308 : /* Remove dummy VLAN and update ethertype */
309 0 : if (EVL_VLANOFTAG(*mtod(m, uint16_t *)) == 0) {
310 0 : m_adj(m, EVL_ENCAPLEN);
311 0 : ehc.ether_type = htons(ETHERTYPE_VLAN);
312 0 : }
313 :
314 0 : M_PREPEND(m, sizeof(*eh), M_NOWAIT);
315 0 : if (m == NULL)
316 0 : return (ENOMEM);
317 :
318 0 : eh = mtod(m, struct ether_header *);
319 0 : memcpy(eh, &ehc, sizeof(*eh));
320 0 : }
321 :
322 0 : ml_enqueue(&ml, m);
323 0 : if_input(ifp, &ml);
324 :
325 0 : return (0);
326 0 : }
327 :
328 : #if NVLAN > 0
329 : extern void vlan_start(struct ifqueue *);
330 :
331 : /*
332 : * This routine handles VLAN tag reinsertion in packets flowing through
333 : * the pseudowire. Also it does the necessary modifications to the VLANs
334 : * to respect the RFC.
335 : */
336 : struct mbuf *
337 0 : mpw_vlan_handle(struct mbuf *m, struct mpw_softc *sc)
338 : {
339 : struct ifnet *ifp;
340 : struct ifvlan *ifv;
341 :
342 : uint16_t type = ETHERTYPE_QINQ;
343 : uint16_t tag = 0;
344 :
345 0 : ifp = if_get(m->m_pkthdr.ph_ifidx);
346 0 : if (ifp != NULL && ifp->if_qstart == vlan_start &&
347 0 : ISSET(ifp->if_flags, IFF_RUNNING)) {
348 0 : ifv = ifp->if_softc;
349 0 : type = ifv->ifv_type;
350 0 : tag = ifv->ifv_tag;
351 0 : }
352 0 : if_put(ifp);
353 :
354 0 : return (vlan_inject(m, type, tag));
355 : }
356 : #endif /* NVLAN */
357 :
358 : void
359 0 : mpw_start(struct ifnet *ifp)
360 : {
361 0 : struct mpw_softc *sc = ifp->if_softc;
362 : struct rtentry *rt;
363 : struct ifnet *p;
364 : struct mbuf *m;
365 : struct shim_hdr *shim;
366 0 : struct sockaddr_storage ss;
367 :
368 0 : if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
369 0 : sc->sc_rshim.shim_label == 0 ||
370 0 : sc->sc_type == IMR_TYPE_NONE) {
371 0 : IFQ_PURGE(&ifp->if_snd);
372 0 : return;
373 : }
374 :
375 0 : rt = rtalloc(sstosa(&sc->sc_nexthop), RT_RESOLVE, ifp->if_rdomain);
376 0 : if (!rtisvalid(rt)) {
377 0 : IFQ_PURGE(&ifp->if_snd);
378 0 : goto rtfree;
379 : }
380 :
381 0 : p = if_get(rt->rt_ifidx);
382 0 : if (p == NULL) {
383 0 : IFQ_PURGE(&ifp->if_snd);
384 0 : goto rtfree;
385 : }
386 :
387 : /*
388 : * XXX: lie about being MPLS, so mpls_output() get the TTL from
389 : * the right place.
390 : */
391 0 : memcpy(&ss, &sc->sc_nexthop, sizeof(sc->sc_nexthop));
392 0 : ss.ss_family = AF_MPLS;
393 :
394 0 : while ((m = ifq_dequeue(&ifp->if_snd)) != NULL) {
395 : #if NBPFILTER > 0
396 0 : if (sc->sc_if.if_bpf)
397 0 : bpf_mtap(sc->sc_if.if_bpf, m, BPF_DIRECTION_OUT);
398 : #endif /* NBPFILTER */
399 :
400 0 : if (sc->sc_type == IMR_TYPE_ETHERNET_TAGGED) {
401 : #if NVLAN > 0
402 0 : m = mpw_vlan_handle(m, sc);
403 0 : if (m == NULL) {
404 0 : ifp->if_oerrors++;
405 0 : continue;
406 : }
407 : #else
408 : /* Ethernet tagged doesn't work without VLANs'*/
409 : m_freem(m);
410 : continue;
411 : #endif /* NVLAN */
412 : }
413 :
414 0 : if (sc->sc_flags & IMR_FLAG_CONTROLWORD) {
415 0 : M_PREPEND(m, sizeof(*shim), M_NOWAIT);
416 0 : if (m == NULL)
417 0 : continue;
418 :
419 0 : shim = mtod(m, struct shim_hdr *);
420 0 : memset(shim, 0, sizeof(*shim));
421 0 : }
422 :
423 0 : M_PREPEND(m, sizeof(*shim), M_NOWAIT);
424 0 : if (m == NULL)
425 0 : continue;
426 :
427 0 : shim = mtod(m, struct shim_hdr *);
428 0 : shim->shim_label = htonl(mpls_defttl) & MPLS_TTL_MASK;
429 0 : shim->shim_label |= sc->sc_rshim.shim_label;
430 :
431 0 : m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
432 :
433 0 : mpls_output(p, m, sstosa(&ss), rt);
434 : }
435 :
436 0 : if_put(p);
437 : rtfree:
438 0 : rtfree(rt);
439 0 : }
|