1 /*- 2 * Copyright (c) 2015 Dmitry Chagin <dchagin@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/kernel.h> 29 #include <sys/ctype.h> 30 #include <sys/eventhandler.h> 31 #include <sys/jail.h> 32 #include <sys/socket.h> 33 #include <sys/sysctl.h> 34 #include <net/if.h> 35 #include <net/if_dl.h> 36 #include <net/if_types.h> 37 #include <net/if_var.h> 38 #include <net/if_private.h> 39 #include <net/vnet.h> 40 41 #include <compat/linux/linux.h> 42 #include <compat/linux/linux_common.h> 43 #include <compat/linux/linux_mib.h> 44 45 _Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ"); 46 47 static bool use_real_ifnames = false; 48 SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN, 49 &use_real_ifnames, 0, 50 "Use FreeBSD interface names instead of generating ethN aliases"); 51 52 VNET_DEFINE_STATIC(struct unrhdr *, linux_eth_unr); 53 #define V_linux_eth_unr VNET(linux_eth_unr) 54 55 static eventhandler_tag ifnet_arrival_tag; 56 static eventhandler_tag ifnet_departure_tag; 57 58 static void 59 linux_ifnet_arrival(void *arg __unused, struct ifnet *ifp) 60 { 61 if (ifp->if_type == IFT_ETHER) 62 ifp->if_linux_ethno = alloc_unr(V_linux_eth_unr); 63 } 64 65 static void 66 linux_ifnet_departure(void *arg __unused, struct ifnet *ifp) 67 { 68 if (ifp->if_type == IFT_ETHER) 69 free_unr(V_linux_eth_unr, ifp->if_linux_ethno); 70 } 71 72 void 73 linux_ifnet_init(void) 74 { 75 ifnet_arrival_tag = EVENTHANDLER_REGISTER(ifnet_arrival_event, 76 linux_ifnet_arrival, NULL, EVENTHANDLER_PRI_FIRST); 77 ifnet_departure_tag = EVENTHANDLER_REGISTER(ifnet_departure_event, 78 linux_ifnet_departure, NULL, EVENTHANDLER_PRI_LAST); 79 } 80 81 void 82 linux_ifnet_uninit(void) 83 { 84 EVENTHANDLER_DEREGISTER(ifnet_arrival_event, ifnet_arrival_tag); 85 EVENTHANDLER_DEREGISTER(ifnet_departure_event, ifnet_departure_tag); 86 } 87 88 static void 89 linux_ifnet_vnet_init(void *arg __unused) 90 { 91 struct epoch_tracker et; 92 struct if_iter it; 93 if_t ifp; 94 95 V_linux_eth_unr = new_unrhdr(0, INT_MAX, NULL); 96 NET_EPOCH_ENTER(et); 97 for (ifp = if_iter_start(&it); ifp != NULL; ifp = if_iter_next(&it)) 98 linux_ifnet_arrival(NULL, ifp); 99 NET_EPOCH_EXIT(et); 100 } 101 VNET_SYSINIT(linux_ifnet_vnet_init, SI_SUB_PROTO_IF, SI_ORDER_ANY, 102 linux_ifnet_vnet_init, NULL); 103 104 static void 105 linux_ifnet_vnet_uninit(void *arg __unused) 106 { 107 /* 108 * All cloned interfaces are already gone at this point, as well 109 * as interfaces that were if_vmove'd into this vnet. However, 110 * if a jail has created IFT_ETHER interfaces in self, or has had 111 * physical Ethernet drivers attached in self, than we may have 112 * allocated entries in the unr(9), so clear it to avoid KASSERT. 113 */ 114 clear_unrhdr(V_linux_eth_unr); 115 delete_unrhdr(V_linux_eth_unr); 116 } 117 VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, 118 linux_ifnet_vnet_uninit, NULL); 119 120 /* 121 * Translate a FreeBSD interface name to a Linux interface name 122 * by interface index, and return the number of bytes copied to lxname. 123 */ 124 int 125 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) 126 { 127 struct epoch_tracker et; 128 struct ifnet *ifp; 129 int ret; 130 131 ret = 0; 132 CURVNET_SET(TD_TO_VNET(curthread)); 133 NET_EPOCH_ENTER(et); 134 ifp = ifnet_byindex(idx); 135 if (ifp != NULL) 136 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); 137 NET_EPOCH_EXIT(et); 138 CURVNET_RESTORE(); 139 return (ret); 140 } 141 142 /* 143 * Translate a FreeBSD interface name to a Linux interface name, 144 * and return the number of bytes copied to lxname, 0 if interface 145 * not found, -1 on error. 146 */ 147 int 148 ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len) 149 { 150 /* 151 * Linux loopback interface name is lo (not lo0), 152 * we translate lo to lo0, loX to loX. 153 */ 154 if (ifp->if_type == IFT_LOOP && 155 strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0) 156 return (strlcpy(lxname, "lo", len)); 157 158 /* Short-circuit non ethernet interfaces. */ 159 if (ifp->if_type != IFT_ETHER || use_real_ifnames) 160 return (strlcpy(lxname, ifp->if_xname, len)); 161 162 /* Determine the (relative) unit number for ethernet interfaces. */ 163 return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno)); 164 } 165 166 /* 167 * Translate a Linux interface name to a FreeBSD interface name, 168 * and return the associated ifnet structure 169 * bsdname and lxname need to be least IFNAMSIZ bytes long, but 170 * can point to the same buffer. 171 */ 172 struct ifname_linux_to_ifp_cb_s { 173 bool is_lo; 174 bool is_eth; 175 int unit; 176 const char *lxname; 177 if_t ifp; 178 }; 179 180 static int 181 ifname_linux_to_ifp_cb(if_t ifp, void *arg) 182 { 183 struct ifname_linux_to_ifp_cb_s *cbs = arg; 184 185 NET_EPOCH_ASSERT(); 186 187 /* 188 * Allow Linux programs to use FreeBSD names. Don't presume 189 * we never have an interface named "eth", so don't make 190 * the test optional based on is_eth. 191 */ 192 if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) 193 goto out; 194 if (cbs->is_eth && ifp->if_type == IFT_ETHER && 195 ifp->if_linux_ethno == cbs->unit) 196 goto out; 197 if (cbs->is_lo && ifp->if_type == IFT_LOOP) 198 goto out; 199 return (0); 200 201 out: 202 cbs->ifp = ifp; 203 return (1); 204 } 205 206 struct ifnet * 207 ifname_linux_to_ifp(const char *lxname) 208 { 209 struct ifname_linux_to_ifp_cb_s arg = { 210 .lxname = lxname, 211 }; 212 int len; 213 char *ep; 214 215 NET_EPOCH_ASSERT(); 216 217 for (len = 0; len < LINUX_IFNAMSIZ; ++len) 218 if (!isalpha(lxname[len]) || lxname[len] == '\0') 219 break; 220 if (len == 0 || len == LINUX_IFNAMSIZ) 221 return (NULL); 222 /* 223 * Linux loopback interface name is lo (not lo0), 224 * we translate lo to lo0, loX to loX. 225 */ 226 arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); 227 arg.unit = (int)strtoul(lxname + len, &ep, 10); 228 if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && 229 arg.is_lo == 0) 230 return (NULL); 231 arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); 232 233 if_foreach(ifname_linux_to_ifp_cb, &arg); 234 return (arg.ifp); 235 } 236 237 int 238 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) 239 { 240 struct epoch_tracker et; 241 struct ifnet *ifp; 242 243 CURVNET_SET(TD_TO_VNET(td)); 244 NET_EPOCH_ENTER(et); 245 ifp = ifname_linux_to_ifp(lxname); 246 if (ifp != NULL && bsdname != NULL) 247 strlcpy(bsdname, if_name(ifp), IFNAMSIZ); 248 NET_EPOCH_EXIT(et); 249 CURVNET_RESTORE(); 250 return (ifp != NULL ? 0 : EINVAL); 251 } 252 253 unsigned short 254 linux_ifflags(struct ifnet *ifp) 255 { 256 unsigned short flags; 257 258 NET_EPOCH_ASSERT(); 259 260 flags = if_getflags(ifp) | if_getdrvflags(ifp); 261 return (bsd_to_linux_ifflags(flags)); 262 } 263 264 unsigned short 265 bsd_to_linux_ifflags(int fl) 266 { 267 unsigned short flags = 0; 268 269 if (fl & IFF_UP) 270 flags |= LINUX_IFF_UP; 271 if (fl & IFF_BROADCAST) 272 flags |= LINUX_IFF_BROADCAST; 273 if (fl & IFF_DEBUG) 274 flags |= LINUX_IFF_DEBUG; 275 if (fl & IFF_LOOPBACK) 276 flags |= LINUX_IFF_LOOPBACK; 277 if (fl & IFF_POINTOPOINT) 278 flags |= LINUX_IFF_POINTOPOINT; 279 if (fl & IFF_DRV_RUNNING) 280 flags |= LINUX_IFF_RUNNING; 281 if (fl & IFF_NOARP) 282 flags |= LINUX_IFF_NOARP; 283 if (fl & IFF_PROMISC) 284 flags |= LINUX_IFF_PROMISC; 285 if (fl & IFF_ALLMULTI) 286 flags |= LINUX_IFF_ALLMULTI; 287 if (fl & IFF_MULTICAST) 288 flags |= LINUX_IFF_MULTICAST; 289 return (flags); 290 } 291 292 static u_int 293 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) 294 { 295 struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; 296 struct l_sockaddr *lsa = arg; 297 298 if (count > 0) 299 return (0); 300 if (sdl->sdl_type != IFT_ETHER) 301 return (0); 302 bzero(lsa, sizeof(*lsa)); 303 lsa->sa_family = LINUX_ARPHRD_ETHER; 304 bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); 305 return (1); 306 } 307 308 int 309 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) 310 { 311 312 NET_EPOCH_ASSERT(); 313 314 if (ifp->if_type == IFT_LOOP) { 315 bzero(lsa, sizeof(*lsa)); 316 lsa->sa_family = LINUX_ARPHRD_LOOPBACK; 317 return (0); 318 } 319 if (ifp->if_type != IFT_ETHER) 320 return (ENOENT); 321 if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) 322 return (0); 323 return (ENOENT); 324 } 325