Line data Source code
1 : /* $OpenBSD: if_mpe.c,v 1.64 2018/01/09 15:24:24 bluhm Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@spootnik.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 : #include "mpe.h"
19 :
20 : #include <sys/param.h>
21 : #include <sys/systm.h>
22 : #include <sys/mbuf.h>
23 : #include <sys/socket.h>
24 : #include <sys/sockio.h>
25 : #include <sys/ioctl.h>
26 :
27 : #include <net/if.h>
28 : #include <net/if_dl.h>
29 : #include <net/if_var.h>
30 : #include <net/if_types.h>
31 : #include <net/netisr.h>
32 : #include <net/route.h>
33 :
34 : #include <netinet/in.h>
35 : #include <netinet/ip.h>
36 :
37 : #ifdef INET6
38 : #include <netinet/ip6.h>
39 : #endif /* INET6 */
40 :
41 : #include "bpfilter.h"
42 : #if NBPFILTER > 0
43 : #include <net/bpf.h>
44 : #endif
45 :
46 : #include <netmpls/mpls.h>
47 :
48 : #ifdef MPLS_DEBUG
49 : #define DPRINTF(x) do { if (mpedebug) printf x ; } while (0)
50 : #else
51 : #define DPRINTF(x)
52 : #endif
53 :
54 : void mpeattach(int);
55 : int mpeoutput(struct ifnet *, struct mbuf *, struct sockaddr *,
56 : struct rtentry *);
57 : int mpeioctl(struct ifnet *, u_long, caddr_t);
58 : void mpestart(struct ifnet *);
59 : int mpe_clone_create(struct if_clone *, int);
60 : int mpe_clone_destroy(struct ifnet *);
61 :
62 : LIST_HEAD(, mpe_softc) mpeif_list;
63 : struct if_clone mpe_cloner =
64 : IF_CLONE_INITIALIZER("mpe", mpe_clone_create, mpe_clone_destroy);
65 :
66 : extern int mpls_mapttl_ip;
67 : #ifdef INET6
68 : extern int mpls_mapttl_ip6;
69 : #endif
70 :
71 : void
72 0 : mpeattach(int nmpe)
73 : {
74 0 : LIST_INIT(&mpeif_list);
75 0 : if_clone_attach(&mpe_cloner);
76 0 : }
77 :
78 : int
79 0 : mpe_clone_create(struct if_clone *ifc, int unit)
80 : {
81 : struct ifnet *ifp;
82 : struct mpe_softc *mpeif;
83 :
84 0 : mpeif = malloc(sizeof(*mpeif), M_DEVBUF, M_WAITOK|M_ZERO);
85 0 : mpeif->sc_unit = unit;
86 0 : ifp = &mpeif->sc_if;
87 0 : snprintf(ifp->if_xname, sizeof ifp->if_xname, "mpe%d", unit);
88 0 : ifp->if_flags = IFF_POINTOPOINT;
89 0 : ifp->if_xflags = IFXF_CLONED;
90 0 : ifp->if_softc = mpeif;
91 0 : ifp->if_mtu = MPE_MTU;
92 0 : ifp->if_ioctl = mpeioctl;
93 0 : ifp->if_output = mpeoutput;
94 0 : ifp->if_start = mpestart;
95 0 : ifp->if_type = IFT_MPLS;
96 0 : ifp->if_hdrlen = MPE_HDRLEN;
97 0 : IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
98 0 : if_attach(ifp);
99 0 : if_alloc_sadl(ifp);
100 : #if NBPFILTER > 0
101 0 : bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(u_int32_t));
102 : #endif
103 :
104 0 : mpeif->sc_ifa.ifa_ifp = ifp;
105 0 : mpeif->sc_ifa.ifa_addr = sdltosa(ifp->if_sadl);
106 0 : mpeif->sc_smpls.smpls_len = sizeof(mpeif->sc_smpls);
107 0 : mpeif->sc_smpls.smpls_family = AF_MPLS;
108 :
109 0 : LIST_INSERT_HEAD(&mpeif_list, mpeif, sc_list);
110 :
111 0 : return (0);
112 : }
113 :
114 : int
115 0 : mpe_clone_destroy(struct ifnet *ifp)
116 : {
117 0 : struct mpe_softc *mpeif = ifp->if_softc;
118 :
119 0 : LIST_REMOVE(mpeif, sc_list);
120 :
121 0 : if (mpeif->sc_smpls.smpls_label) {
122 0 : rt_ifa_del(&mpeif->sc_ifa, RTF_MPLS,
123 0 : smplstosa(&mpeif->sc_smpls));
124 0 : }
125 :
126 0 : if_detach(ifp);
127 0 : free(mpeif, M_DEVBUF, sizeof *mpeif);
128 0 : return (0);
129 : }
130 :
131 : struct sockaddr_storage mpedst;
132 : /*
133 : * Start output on the mpe interface.
134 : */
135 : void
136 0 : mpestart(struct ifnet *ifp0)
137 : {
138 : struct mbuf *m;
139 0 : struct sockaddr *sa = sstosa(&mpedst);
140 : sa_family_t af;
141 : struct rtentry *rt;
142 : struct ifnet *ifp;
143 :
144 0 : for (;;) {
145 0 : IFQ_DEQUEUE(&ifp0->if_snd, m);
146 0 : if (m == NULL)
147 : return;
148 :
149 0 : af = *mtod(m, sa_family_t *);
150 0 : m_adj(m, sizeof(af));
151 0 : switch (af) {
152 : case AF_INET:
153 0 : bzero(sa, sizeof(struct sockaddr_in));
154 0 : satosin(sa)->sin_family = af;
155 0 : satosin(sa)->sin_len = sizeof(struct sockaddr_in);
156 0 : bcopy(mtod(m, caddr_t), &satosin(sa)->sin_addr,
157 : sizeof(in_addr_t));
158 0 : m_adj(m, sizeof(in_addr_t));
159 : break;
160 : default:
161 0 : m_freem(m);
162 0 : continue;
163 : }
164 :
165 0 : rt = rtalloc(sa, RT_RESOLVE, 0);
166 0 : if (!rtisvalid(rt)) {
167 0 : m_freem(m);
168 0 : rtfree(rt);
169 0 : continue;
170 : }
171 :
172 0 : ifp = if_get(rt->rt_ifidx);
173 0 : if (ifp == NULL) {
174 0 : m_freem(m);
175 0 : rtfree(rt);
176 0 : continue;
177 : }
178 : #if NBPFILTER > 0
179 0 : if (ifp0->if_bpf) {
180 : /* remove MPLS label before passing packet to bpf */
181 0 : m->m_data += sizeof(struct shim_hdr);
182 0 : m->m_len -= sizeof(struct shim_hdr);
183 0 : m->m_pkthdr.len -= sizeof(struct shim_hdr);
184 0 : bpf_mtap_af(ifp0->if_bpf, af, m, BPF_DIRECTION_OUT);
185 0 : m->m_data -= sizeof(struct shim_hdr);
186 0 : m->m_len += sizeof(struct shim_hdr);
187 0 : m->m_pkthdr.len += sizeof(struct shim_hdr);
188 0 : }
189 : #endif
190 : /* XXX lie, but mpls_output looks only at sa_family */
191 0 : sa->sa_family = AF_MPLS;
192 :
193 0 : mpls_output(ifp, m, sa, rt);
194 0 : if_put(ifp);
195 0 : rtfree(rt);
196 : }
197 0 : }
198 :
199 : int
200 0 : mpeoutput(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
201 : struct rtentry *rt)
202 : {
203 0 : struct shim_hdr shim;
204 : int error;
205 : int off;
206 0 : in_addr_t addr;
207 : u_int8_t op = 0;
208 :
209 : #ifdef DIAGNOSTIC
210 0 : if (ifp->if_rdomain != rtable_l2(m->m_pkthdr.ph_rtableid)) {
211 0 : printf("%s: trying to send packet on wrong domain. "
212 0 : "if %d vs. mbuf %d\n", ifp->if_xname,
213 0 : ifp->if_rdomain, rtable_l2(m->m_pkthdr.ph_rtableid));
214 0 : }
215 : #endif
216 0 : m->m_pkthdr.ph_ifidx = ifp->if_index;
217 : /* XXX assumes MPLS is always in rdomain 0 */
218 0 : m->m_pkthdr.ph_rtableid = 0;
219 :
220 : error = 0;
221 0 : switch (dst->sa_family) {
222 : case AF_INET:
223 0 : if (!rt || !(rt->rt_flags & RTF_MPLS)) {
224 0 : m_freem(m);
225 : error = ENETUNREACH;
226 0 : goto out;
227 : }
228 0 : shim.shim_label =
229 0 : ((struct rt_mpls *)rt->rt_llinfo)->mpls_label;
230 0 : shim.shim_label |= MPLS_BOS_MASK;
231 0 : op = ((struct rt_mpls *)rt->rt_llinfo)->mpls_operation;
232 0 : if (op != MPLS_OP_PUSH) {
233 0 : m_freem(m);
234 : error = ENETUNREACH;
235 0 : goto out;
236 : }
237 0 : if (mpls_mapttl_ip) {
238 : struct ip *ip;
239 0 : ip = mtod(m, struct ip *);
240 0 : shim.shim_label |= htonl(ip->ip_ttl) & MPLS_TTL_MASK;
241 0 : } else
242 0 : shim.shim_label |= htonl(mpls_defttl) & MPLS_TTL_MASK;
243 : off = sizeof(sa_family_t) + sizeof(in_addr_t);
244 0 : M_PREPEND(m, sizeof(shim) + off, M_DONTWAIT);
245 0 : if (m == NULL) {
246 : error = ENOBUFS;
247 0 : goto out;
248 : }
249 0 : *mtod(m, sa_family_t *) = AF_INET;
250 0 : addr = satosin(rt->rt_gateway)->sin_addr.s_addr;
251 0 : m_copyback(m, sizeof(sa_family_t), sizeof(in_addr_t),
252 : &addr, M_NOWAIT);
253 : break;
254 : default:
255 0 : m_freem(m);
256 : error = EPFNOSUPPORT;
257 0 : goto out;
258 : }
259 :
260 0 : m_copyback(m, off, sizeof(shim), (caddr_t)&shim, M_NOWAIT);
261 :
262 0 : error = if_enqueue(ifp, m);
263 : out:
264 0 : if (error)
265 0 : ifp->if_oerrors++;
266 0 : return (error);
267 0 : }
268 :
269 : int
270 0 : mpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
271 : {
272 : struct mpe_softc *ifm;
273 : struct ifreq *ifr;
274 0 : struct shim_hdr shim;
275 : int error = 0;
276 :
277 0 : ifr = (struct ifreq *)data;
278 0 : switch (cmd) {
279 : case SIOCSIFADDR:
280 0 : if (!ISSET(ifp->if_flags, IFF_UP))
281 0 : if_up(ifp);
282 : break;
283 : case SIOCSIFFLAGS:
284 0 : if (ifp->if_flags & IFF_UP)
285 0 : ifp->if_flags |= IFF_RUNNING;
286 : else
287 0 : ifp->if_flags &= ~IFF_RUNNING;
288 : break;
289 : case SIOCSIFMTU:
290 0 : if (ifr->ifr_mtu < MPE_MTU_MIN ||
291 0 : ifr->ifr_mtu > MPE_MTU_MAX)
292 0 : error = EINVAL;
293 : else
294 0 : ifp->if_mtu = ifr->ifr_mtu;
295 : break;
296 : case SIOCGETLABEL:
297 0 : ifm = ifp->if_softc;
298 0 : shim.shim_label =
299 0 : ((ntohl(ifm->sc_smpls.smpls_label & MPLS_LABEL_MASK)) >>
300 : MPLS_LABEL_OFFSET);
301 0 : error = copyout(&shim, ifr->ifr_data, sizeof(shim));
302 0 : break;
303 : case SIOCSETLABEL:
304 0 : ifm = ifp->if_softc;
305 0 : if ((error = copyin(ifr->ifr_data, &shim, sizeof(shim))))
306 : break;
307 0 : if (shim.shim_label > MPLS_LABEL_MAX ||
308 0 : shim.shim_label <= MPLS_LABEL_RESERVED_MAX) {
309 : error = EINVAL;
310 0 : break;
311 : }
312 0 : shim.shim_label = htonl(shim.shim_label << MPLS_LABEL_OFFSET);
313 0 : if (ifm->sc_smpls.smpls_label == shim.shim_label)
314 : break;
315 0 : LIST_FOREACH(ifm, &mpeif_list, sc_list) {
316 0 : if (ifm != ifp->if_softc &&
317 0 : ifm->sc_smpls.smpls_label == shim.shim_label) {
318 : error = EEXIST;
319 0 : break;
320 : }
321 : }
322 0 : if (error)
323 : break;
324 : /*
325 : * force interface up for now,
326 : * linkstate of MPLS route is not tracked
327 : */
328 0 : if (!ISSET(ifp->if_flags, IFF_UP))
329 0 : if_up(ifp);
330 0 : ifm = ifp->if_softc;
331 0 : if (ifm->sc_smpls.smpls_label) {
332 : /* remove old MPLS route */
333 0 : rt_ifa_del(&ifm->sc_ifa, RTF_MPLS,
334 0 : smplstosa(&ifm->sc_smpls));
335 0 : }
336 : /* add new MPLS route */
337 0 : ifm->sc_smpls.smpls_label = shim.shim_label;
338 0 : error = rt_ifa_add(&ifm->sc_ifa, RTF_MPLS,
339 0 : smplstosa(&ifm->sc_smpls));
340 0 : if (error) {
341 0 : ifm->sc_smpls.smpls_label = 0;
342 0 : break;
343 : }
344 : break;
345 : case SIOCSIFRDOMAIN:
346 : /* must readd the MPLS "route" for our label */
347 : /* XXX does not make sense, the MPLS route is on rtable 0 */
348 0 : ifm = ifp->if_softc;
349 0 : if (ifr->ifr_rdomainid != ifp->if_rdomain) {
350 0 : if (ifm->sc_smpls.smpls_label) {
351 0 : rt_ifa_add(&ifm->sc_ifa, RTF_MPLS,
352 0 : smplstosa(&ifm->sc_smpls));
353 0 : }
354 : }
355 : /* return with ENOTTY so that the parent handler finishes */
356 0 : return (ENOTTY);
357 : default:
358 0 : return (ENOTTY);
359 : }
360 :
361 0 : return (error);
362 0 : }
363 :
364 : void
365 0 : mpe_input(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
366 : u_int8_t ttl)
367 : {
368 : struct ip *ip;
369 : int hlen;
370 :
371 : /* label -> AF lookup */
372 :
373 0 : if (mpls_mapttl_ip) {
374 0 : if (m->m_len < sizeof (struct ip) &&
375 0 : (m = m_pullup(m, sizeof(struct ip))) == NULL)
376 0 : return;
377 0 : ip = mtod(m, struct ip *);
378 0 : hlen = ip->ip_hl << 2;
379 0 : if (m->m_len < hlen) {
380 0 : if ((m = m_pullup(m, hlen)) == NULL)
381 0 : return;
382 0 : ip = mtod(m, struct ip *);
383 0 : }
384 :
385 0 : if (in_cksum(m, hlen) != 0) {
386 0 : m_freem(m);
387 0 : return;
388 : }
389 :
390 : /* set IP ttl from MPLS ttl */
391 0 : ip->ip_ttl = ttl;
392 :
393 : /* recalculate checksum */
394 0 : ip->ip_sum = 0;
395 0 : ip->ip_sum = in_cksum(m, hlen);
396 0 : }
397 :
398 : /* new receive if and move into correct rtable */
399 0 : m->m_pkthdr.ph_ifidx = ifp->if_index;
400 0 : m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
401 :
402 : #if NBPFILTER > 0
403 0 : if (ifp->if_bpf)
404 0 : bpf_mtap_af(ifp->if_bpf, AF_INET, m, BPF_DIRECTION_IN);
405 : #endif
406 :
407 0 : ipv4_input(ifp, m);
408 0 : }
409 :
410 : #ifdef INET6
411 : void
412 0 : mpe_input6(struct mbuf *m, struct ifnet *ifp, struct sockaddr_mpls *smpls,
413 : u_int8_t ttl)
414 : {
415 : struct ip6_hdr *ip6hdr;
416 :
417 : /* label -> AF lookup */
418 :
419 0 : if (mpls_mapttl_ip6) {
420 0 : if (m->m_len < sizeof (struct ip6_hdr) &&
421 0 : (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL)
422 0 : return;
423 :
424 0 : ip6hdr = mtod(m, struct ip6_hdr *);
425 :
426 : /* set IPv6 ttl from MPLS ttl */
427 0 : ip6hdr->ip6_hlim = ttl;
428 0 : }
429 :
430 : /* new receive if and move into correct rtable */
431 0 : m->m_pkthdr.ph_ifidx = ifp->if_index;
432 0 : m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
433 :
434 : #if NBPFILTER > 0
435 0 : if (ifp->if_bpf)
436 0 : bpf_mtap_af(ifp->if_bpf, AF_INET6, m, BPF_DIRECTION_IN);
437 : #endif
438 :
439 0 : ipv6_input(ifp, m);
440 0 : }
441 : #endif /* INET6 */
|