1 /* $OpenBSD: if_rport.c,v 1.1 2024/08/31 04:17:14 dlg Exp $ */ 2 3 /* 4 * Copyright (c) 2023 David Gwynne <dlg@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 <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/mbuf.h> 22 #include <sys/socket.h> 23 #include <sys/sockio.h> 24 #include <sys/syslog.h> 25 #include <sys/queue.h> 26 27 #include <net/if.h> 28 #include <net/if_var.h> 29 #include <net/if_types.h> 30 #include <net/route.h> 31 32 #include <netinet/in.h> 33 #include <netinet/in_var.h> 34 #include <netinet/ip.h> 35 #include <netinet/ip_var.h> 36 #include <netinet/ip_ipip.h> 37 #include <netinet/ip_ecn.h> 38 39 #ifdef INET6 40 #include <netinet6/in6_var.h> 41 #include <netinet/ip6.h> 42 #include <netinet6/ip6_var.h> 43 #endif /* INET6 */ 44 45 #include "bpfilter.h" 46 #if NBPFILTER > 0 47 #include <net/bpf.h> 48 #endif 49 50 #ifdef MPLS 51 #include <netmpls/mpls.h> 52 #endif 53 54 #include "pf.h" 55 #if NPF > 0 56 #include <net/pfvar.h> 57 #endif 58 59 #define RPORT_MTU_MIN 1280 60 #define RPORT_MTU_MAX 32768 /* LOMTU, but could be higher */ 61 #define RPORT_MTU_DEFAULT RPORT_MTU_MAX 62 63 struct rport_softc { 64 struct ifnet sc_if; 65 66 unsigned int sc_peer_idx; 67 }; 68 69 static int rport_clone_create(struct if_clone *, int); 70 static int rport_clone_destroy(struct ifnet *); 71 72 static int rport_ioctl(struct ifnet *, u_long, caddr_t); 73 static int rport_output(struct ifnet *, struct mbuf *, struct sockaddr *, 74 struct rtentry *); 75 static int rport_enqueue(struct ifnet *, struct mbuf *); 76 static void rport_start(struct ifqueue *); 77 static void rport_input(struct ifnet *, struct mbuf *); 78 79 static int rport_up(struct rport_softc *); 80 static int rport_down(struct rport_softc *); 81 82 static int rport_set_parent(struct rport_softc *, 83 const struct if_parent *); 84 static int rport_get_parent(struct rport_softc *, struct if_parent *); 85 static int rport_del_parent(struct rport_softc *); 86 87 static struct if_clone rport_cloner = 88 IF_CLONE_INITIALIZER("rport", rport_clone_create, rport_clone_destroy); 89 90 static struct rwlock rport_interfaces_lock = 91 RWLOCK_INITIALIZER("rports"); 92 93 void 94 rportattach(int count) 95 { 96 if_clone_attach(&rport_cloner); 97 } 98 99 static int 100 rport_clone_create(struct if_clone *ifc, int unit) 101 { 102 struct rport_softc *sc; 103 struct ifnet *ifp; 104 105 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK|M_ZERO); 106 ifp = &sc->sc_if; 107 108 snprintf(ifp->if_xname, sizeof(ifp->if_xname), 109 "%s%d", ifc->ifc_name, unit); 110 111 ifp->if_mtu = RPORT_MTU_DEFAULT; 112 ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; 113 ifp->if_xflags = IFXF_CLONED | IFXF_MPSAFE; 114 ifp->if_ioctl = rport_ioctl; 115 ifp->if_bpf_mtap = p2p_bpf_mtap; 116 ifp->if_output = rport_output; 117 ifp->if_enqueue = rport_enqueue; 118 ifp->if_qstart = rport_start; 119 ifp->if_input = rport_input; 120 ifp->if_rtrequest = p2p_rtrequest; 121 ifp->if_type = IFT_TUNNEL; 122 ifp->if_softc = sc; 123 124 if_attach(ifp); 125 if_alloc_sadl(ifp); 126 if_counters_alloc(ifp); 127 128 #if NBPFILTER > 0 129 bpfattach(&ifp->if_bpf, ifp, DLT_LOOP, sizeof(uint32_t)); 130 #endif 131 132 return (0); 133 } 134 135 int 136 rport_clone_destroy(struct ifnet *ifp) 137 { 138 struct rport_softc *sc = ifp->if_softc; 139 140 NET_LOCK(); 141 if (ISSET(ifp->if_flags, IFF_RUNNING)) 142 rport_down(sc); 143 rport_del_parent(sc); 144 NET_UNLOCK(); 145 146 if_detach(ifp); 147 148 free(sc, M_DEVBUF, sizeof(*sc)); 149 150 return (0); 151 } 152 153 static int 154 rport_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, 155 struct rtentry *rt) 156 { 157 struct m_tag *mtag; 158 int error = 0; 159 160 if (!ISSET(ifp->if_flags, IFF_RUNNING)) { 161 error = ENETDOWN; 162 goto drop; 163 } 164 165 switch (dst->sa_family) { 166 case AF_INET: 167 #ifdef INET6 168 case AF_INET6: 169 #endif 170 #ifdef MPLS 171 case AF_MPLS: 172 #endif 173 break; 174 default: 175 error = EAFNOSUPPORT; 176 goto drop; 177 } 178 179 /* Try to limit infinite recursion through misconfiguration. */ 180 mtag = NULL; 181 while ((mtag = m_tag_find(m, PACKET_TAG_GRE, mtag)) != NULL) { 182 if (*(int *)(mtag + 1) == ifp->if_index) { 183 error = EIO; 184 goto drop; 185 } 186 } 187 188 mtag = m_tag_get(PACKET_TAG_GRE, sizeof(ifp->if_index), M_NOWAIT); 189 if (mtag == NULL) { 190 error = ENOBUFS; 191 goto drop; 192 } 193 *(int *)(mtag + 1) = ifp->if_index; 194 m_tag_prepend(m, mtag); 195 196 m->m_flags &= ~(M_BCAST|M_MCAST); 197 m->m_pkthdr.ph_family = dst->sa_family; 198 #if NPF > 0 199 pf_pkt_addr_changed(m); 200 #endif 201 202 error = if_enqueue(ifp, m); 203 if (error) 204 counters_inc(ifp->if_counters, ifc_oerrors); 205 206 return (error); 207 208 drop: 209 m_freem(m); 210 return (error); 211 } 212 213 static int 214 rport_enqueue(struct ifnet *ifp, struct mbuf *m) 215 { 216 struct ifqueue *ifq = &ifp->if_snd; 217 int error; 218 219 error = ifq_enqueue(ifq, m); 220 if (error) 221 return (error); 222 223 /* 224 * always defer handover of packets to the peer to the ifq 225 * bundle task to provide control over the NET_LOCK scope. 226 */ 227 task_add(ifq->ifq_softnet, &ifq->ifq_bundle); 228 229 return (0); 230 } 231 232 static void 233 rport_start(struct ifqueue *ifq) 234 { 235 struct ifnet *ifp = ifq->ifq_if; 236 struct rport_softc *sc = ifp->if_softc; 237 struct ifnet *ifp0; 238 struct mbuf *m; 239 240 ifp0 = if_get(sc->sc_peer_idx); 241 if (ifp0 == NULL || !ISSET(ifp0->if_flags, IFF_RUNNING)) { 242 ifq_purge(ifq); 243 if_put(ifp0); 244 return; 245 } 246 247 NET_LOCK_SHARED(); 248 while ((m = ifq_dequeue(ifq)) != NULL) { 249 #if NBPFILTER > 0 250 caddr_t if_bpf = READ_ONCE(ifp->if_bpf); 251 if (if_bpf && bpf_mtap_af(if_bpf, m->m_pkthdr.ph_family, 252 m, BPF_DIRECTION_OUT)) { 253 m_freem(m); 254 continue; 255 } 256 #endif 257 258 if_vinput(ifp0, m); 259 } 260 NET_UNLOCK_SHARED(); 261 262 if_put(ifp0); 263 } 264 265 static void 266 rport_input(struct ifnet *ifp, struct mbuf *m) 267 { 268 switch (m->m_pkthdr.ph_family) { 269 case AF_INET: 270 ipv4_input(ifp, m); 271 break; 272 #ifdef INET6 273 case AF_INET6: 274 ipv6_input(ifp, m); 275 break; 276 #endif 277 #ifdef MPLS 278 case AF_MPLS: 279 mpls_input(ifp, m); 280 break; 281 #endif 282 default: 283 counters_inc(ifp->if_counters, ifc_noproto); 284 m_freem(m); 285 break; 286 } 287 } 288 289 static int 290 rport_up(struct rport_softc *sc) 291 { 292 NET_ASSERT_LOCKED(); 293 294 SET(sc->sc_if.if_flags, IFF_RUNNING); 295 296 return (0); 297 } 298 299 static int 300 rport_down(struct rport_softc *sc) 301 { 302 NET_ASSERT_LOCKED(); 303 304 CLR(sc->sc_if.if_flags, IFF_RUNNING); 305 306 return (0); 307 } 308 309 static int 310 rport_set_parent(struct rport_softc *sc, const struct if_parent *p) 311 { 312 struct ifnet *ifp = &sc->sc_if; 313 struct ifnet *ifp0; 314 struct rport_softc *sc0; 315 int error; 316 317 error = rw_enter(&rport_interfaces_lock, RW_WRITE | RW_INTR); 318 if (error != 0) 319 return (error); 320 321 ifp0 = if_unit(p->ifp_parent); 322 if (ifp0 == NULL) { 323 error = EINVAL; 324 goto leave; 325 } 326 327 if (ifp0 == ifp) { 328 error = EINVAL; 329 goto leave; 330 } 331 332 if (ifp0->if_input != rport_input) { 333 error = EPROTONOSUPPORT; 334 goto put; 335 } 336 337 sc0 = ifp0->if_softc; 338 339 if (sc->sc_peer_idx == ifp0->if_index) { 340 /* nop */ 341 KASSERT(sc0->sc_peer_idx == ifp->if_index); 342 goto put; 343 } 344 345 if (sc->sc_peer_idx != 0 || sc0->sc_peer_idx != 0) { 346 error = EBUSY; 347 goto put; 348 } 349 350 /* commit */ 351 sc->sc_peer_idx = ifp0->if_index; 352 sc0->sc_peer_idx = ifp->if_index; 353 354 put: 355 if_put(ifp0); 356 leave: 357 rw_exit(&rport_interfaces_lock); 358 359 return (error); 360 } 361 362 static int 363 rport_get_parent(struct rport_softc *sc, struct if_parent *p) 364 { 365 struct ifnet *ifp0; 366 int error = 0; 367 368 ifp0 = if_get(sc->sc_peer_idx); 369 if (ifp0 == NULL) 370 error = EADDRNOTAVAIL; 371 else { 372 if (strlcpy(p->ifp_parent, ifp0->if_xname, 373 sizeof(p->ifp_parent)) >= sizeof(p->ifp_parent)) 374 panic("%s strlcpy", __func__); 375 } 376 if_put(ifp0); 377 378 return (error); 379 } 380 381 static int 382 rport_del_parent(struct rport_softc *sc) 383 { 384 struct rport_softc *sc0; 385 struct ifnet *ifp0; 386 int error; 387 388 error = rw_enter(&rport_interfaces_lock, RW_WRITE | RW_INTR); 389 if (error != 0) 390 return (error); 391 392 ifp0 = if_get(sc->sc_peer_idx); 393 sc->sc_peer_idx = 0; 394 395 if (ifp0 != NULL) { 396 sc0 = ifp0->if_softc; 397 sc0->sc_peer_idx = 0; 398 } 399 if_put(ifp0); 400 401 rw_exit(&rport_interfaces_lock); 402 403 return (0); 404 } 405 406 static int 407 rport_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 408 { 409 struct rport_softc *sc = ifp->if_softc; 410 struct ifreq *ifr = (struct ifreq *)data; 411 int error = 0; 412 413 switch (cmd) { 414 case SIOCSIFADDR: 415 break; 416 case SIOCSIFFLAGS: 417 if (ISSET(ifp->if_flags, IFF_UP)) { 418 if (!ISSET(ifp->if_flags, IFF_RUNNING)) 419 error = rport_up(sc); 420 } else { 421 if (ISSET(ifp->if_flags, IFF_RUNNING)) 422 error = rport_down(sc); 423 } 424 break; 425 426 case SIOCADDMULTI: 427 case SIOCDELMULTI: 428 break; 429 430 case SIOCSIFMTU: 431 if (ifr->ifr_mtu < RPORT_MTU_MIN || 432 ifr->ifr_mtu > RPORT_MTU_MAX) { 433 error = EINVAL; 434 break; 435 } 436 437 ifp->if_mtu = ifr->ifr_mtu; 438 break; 439 440 case SIOCSIFPARENT: 441 error = rport_set_parent(sc, (struct if_parent *)data); 442 break; 443 case SIOCGIFPARENT: 444 error = rport_get_parent(sc, (struct if_parent *)data); 445 break; 446 case SIOCDIFPARENT: 447 error = rport_del_parent(sc); 448 break; 449 450 default: 451 error = ENOTTY; 452 break; 453 } 454 455 return (error); 456 } 457