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 #ifdef VIMAGE 105 static void 106 linux_ifnet_vnet_uninit(void *arg __unused) 107 { 108 delete_unrhdr(V_linux_eth_unr); 109 } 110 VNET_SYSUNINIT(linux_ifnet_vnet_uninit, SI_SUB_PROTO_IF, SI_ORDER_ANY, 111 linux_ifnet_vnet_uninit, NULL); 112 #endif 113 114 /* 115 * Translate a FreeBSD interface name to a Linux interface name 116 * by interface index, and return the number of bytes copied to lxname. 117 */ 118 int 119 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) 120 { 121 struct epoch_tracker et; 122 struct ifnet *ifp; 123 int ret; 124 125 ret = 0; 126 CURVNET_SET(TD_TO_VNET(curthread)); 127 NET_EPOCH_ENTER(et); 128 ifp = ifnet_byindex(idx); 129 if (ifp != NULL) 130 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); 131 NET_EPOCH_EXIT(et); 132 CURVNET_RESTORE(); 133 return (ret); 134 } 135 136 /* 137 * Translate a FreeBSD interface name to a Linux interface name, 138 * and return the number of bytes copied to lxname, 0 if interface 139 * not found, -1 on error. 140 */ 141 int 142 ifname_bsd_to_linux_ifp(const struct ifnet *ifp, char *lxname, size_t len) 143 { 144 /* 145 * Linux loopback interface name is lo (not lo0), 146 * we translate lo to lo0, loX to loX. 147 */ 148 if (ifp->if_type == IFT_LOOP && 149 strncmp(ifp->if_xname, "lo0", IFNAMSIZ) == 0) 150 return (strlcpy(lxname, "lo", len)); 151 152 /* Short-circuit non ethernet interfaces. */ 153 if (ifp->if_type != IFT_ETHER || use_real_ifnames) 154 return (strlcpy(lxname, ifp->if_xname, len)); 155 156 /* Determine the (relative) unit number for ethernet interfaces. */ 157 return (snprintf(lxname, len, "eth%d", ifp->if_linux_ethno)); 158 } 159 160 /* 161 * Translate a Linux interface name to a FreeBSD interface name, 162 * and return the associated ifnet structure 163 * bsdname and lxname need to be least IFNAMSIZ bytes long, but 164 * can point to the same buffer. 165 */ 166 struct ifname_linux_to_ifp_cb_s { 167 bool is_lo; 168 bool is_eth; 169 int unit; 170 const char *lxname; 171 if_t ifp; 172 }; 173 174 static int 175 ifname_linux_to_ifp_cb(if_t ifp, void *arg) 176 { 177 struct ifname_linux_to_ifp_cb_s *cbs = arg; 178 179 NET_EPOCH_ASSERT(); 180 181 /* 182 * Allow Linux programs to use FreeBSD names. Don't presume 183 * we never have an interface named "eth", so don't make 184 * the test optional based on is_eth. 185 */ 186 if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) 187 goto out; 188 if (cbs->is_eth && ifp->if_type == IFT_ETHER && 189 ifp->if_linux_ethno == cbs->unit) 190 goto out; 191 if (cbs->is_lo && ifp->if_type == IFT_LOOP) 192 goto out; 193 return (0); 194 195 out: 196 cbs->ifp = ifp; 197 return (1); 198 } 199 200 struct ifnet * 201 ifname_linux_to_ifp(const char *lxname) 202 { 203 struct ifname_linux_to_ifp_cb_s arg = { 204 .lxname = lxname, 205 }; 206 int len; 207 char *ep; 208 209 NET_EPOCH_ASSERT(); 210 211 for (len = 0; len < LINUX_IFNAMSIZ; ++len) 212 if (!isalpha(lxname[len]) || lxname[len] == '\0') 213 break; 214 if (len == 0 || len == LINUX_IFNAMSIZ) 215 return (NULL); 216 /* 217 * Linux loopback interface name is lo (not lo0), 218 * we translate lo to lo0, loX to loX. 219 */ 220 arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); 221 arg.unit = (int)strtoul(lxname + len, &ep, 10); 222 if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && 223 arg.is_lo == 0) 224 return (NULL); 225 arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); 226 227 if_foreach(ifname_linux_to_ifp_cb, &arg); 228 return (arg.ifp); 229 } 230 231 int 232 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) 233 { 234 struct epoch_tracker et; 235 struct ifnet *ifp; 236 237 CURVNET_SET(TD_TO_VNET(td)); 238 NET_EPOCH_ENTER(et); 239 ifp = ifname_linux_to_ifp(lxname); 240 if (ifp != NULL && bsdname != NULL) 241 strlcpy(bsdname, if_name(ifp), IFNAMSIZ); 242 NET_EPOCH_EXIT(et); 243 CURVNET_RESTORE(); 244 return (ifp != NULL ? 0 : EINVAL); 245 } 246 247 unsigned short 248 linux_ifflags(struct ifnet *ifp) 249 { 250 unsigned short flags; 251 252 NET_EPOCH_ASSERT(); 253 254 flags = if_getflags(ifp) | if_getdrvflags(ifp); 255 return (bsd_to_linux_ifflags(flags)); 256 } 257 258 unsigned short 259 bsd_to_linux_ifflags(int fl) 260 { 261 unsigned short flags = 0; 262 263 if (fl & IFF_UP) 264 flags |= LINUX_IFF_UP; 265 if (fl & IFF_BROADCAST) 266 flags |= LINUX_IFF_BROADCAST; 267 if (fl & IFF_DEBUG) 268 flags |= LINUX_IFF_DEBUG; 269 if (fl & IFF_LOOPBACK) 270 flags |= LINUX_IFF_LOOPBACK; 271 if (fl & IFF_POINTOPOINT) 272 flags |= LINUX_IFF_POINTOPOINT; 273 if (fl & IFF_DRV_RUNNING) 274 flags |= LINUX_IFF_RUNNING; 275 if (fl & IFF_NOARP) 276 flags |= LINUX_IFF_NOARP; 277 if (fl & IFF_PROMISC) 278 flags |= LINUX_IFF_PROMISC; 279 if (fl & IFF_ALLMULTI) 280 flags |= LINUX_IFF_ALLMULTI; 281 if (fl & IFF_MULTICAST) 282 flags |= LINUX_IFF_MULTICAST; 283 return (flags); 284 } 285 286 static u_int 287 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) 288 { 289 struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; 290 struct l_sockaddr *lsa = arg; 291 292 if (count > 0) 293 return (0); 294 if (sdl->sdl_type != IFT_ETHER) 295 return (0); 296 bzero(lsa, sizeof(*lsa)); 297 lsa->sa_family = LINUX_ARPHRD_ETHER; 298 bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); 299 return (1); 300 } 301 302 int 303 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) 304 { 305 306 NET_EPOCH_ASSERT(); 307 308 if (ifp->if_type == IFT_LOOP) { 309 bzero(lsa, sizeof(*lsa)); 310 lsa->sa_family = LINUX_ARPHRD_LOOPBACK; 311 return (0); 312 } 313 if (ifp->if_type != IFT_ETHER) 314 return (ENOENT); 315 if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) 316 return (0); 317 return (ENOENT); 318 } 319