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/ctype.h> 28 #include <sys/jail.h> 29 #include <sys/socket.h> 30 #include <sys/sysctl.h> 31 #include <net/if.h> 32 #include <net/if_dl.h> 33 #include <net/if_types.h> 34 #include <net/if_var.h> 35 36 #include <compat/linux/linux.h> 37 #include <compat/linux/linux_common.h> 38 #include <compat/linux/linux_mib.h> 39 40 _Static_assert(LINUX_IFNAMSIZ == IFNAMSIZ, "Linux IFNAMSIZ"); 41 42 static bool use_real_ifnames = false; 43 SYSCTL_BOOL(_compat_linux, OID_AUTO, use_real_ifnames, CTLFLAG_RWTUN, 44 &use_real_ifnames, 0, 45 "Use FreeBSD interface names instead of generating ethN aliases"); 46 47 /* 48 * Criteria for interface name translation 49 */ 50 #define IFP_IS_ETH(ifp) (if_gettype(ifp) == IFT_ETHER) 51 #define IFP_IS_LOOP(ifp) (if_gettype(ifp) == IFT_LOOP) 52 53 /* 54 * Translate a FreeBSD interface name to a Linux interface name 55 * by interface name, and return the number of bytes copied to lxname. 56 */ 57 int 58 ifname_bsd_to_linux_name(const char *bsdname, char *lxname, size_t len) 59 { 60 struct epoch_tracker et; 61 struct ifnet *ifp; 62 int ret; 63 64 CURVNET_ASSERT_SET(); 65 66 ret = 0; 67 NET_EPOCH_ENTER(et); 68 ifp = ifunit(bsdname); 69 if (ifp != NULL) 70 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); 71 NET_EPOCH_EXIT(et); 72 return (ret); 73 } 74 75 /* 76 * Translate a FreeBSD interface name to a Linux interface name 77 * by interface index, and return the number of bytes copied to lxname. 78 */ 79 int 80 ifname_bsd_to_linux_idx(u_int idx, char *lxname, size_t len) 81 { 82 struct epoch_tracker et; 83 struct ifnet *ifp; 84 int ret; 85 86 ret = 0; 87 CURVNET_SET(TD_TO_VNET(curthread)); 88 NET_EPOCH_ENTER(et); 89 ifp = ifnet_byindex(idx); 90 if (ifp != NULL) 91 ret = ifname_bsd_to_linux_ifp(ifp, lxname, len); 92 NET_EPOCH_EXIT(et); 93 CURVNET_RESTORE(); 94 return (ret); 95 } 96 97 /* 98 * Translate a FreeBSD interface name to a Linux interface name, 99 * and return the number of bytes copied to lxname, 0 if interface 100 * not found, -1 on error. 101 */ 102 struct ifname_bsd_to_linux_ifp_cb_s { 103 struct ifnet *ifp; 104 int ethno; 105 char *lxname; 106 size_t len; 107 }; 108 109 static int 110 ifname_bsd_to_linux_ifp_cb(if_t ifp, void *arg) 111 { 112 struct ifname_bsd_to_linux_ifp_cb_s *cbs = arg; 113 114 if (ifp == cbs->ifp) 115 return (snprintf(cbs->lxname, cbs->len, "eth%d", cbs->ethno)); 116 if (IFP_IS_ETH(ifp)) 117 cbs->ethno++; 118 return (0); 119 } 120 121 int 122 ifname_bsd_to_linux_ifp(struct ifnet *ifp, char *lxname, size_t len) 123 { 124 struct ifname_bsd_to_linux_ifp_cb_s arg = { 125 .ifp = ifp, 126 .ethno = 0, 127 .lxname = lxname, 128 .len = len, 129 }; 130 131 NET_EPOCH_ASSERT(); 132 133 /* 134 * Linux loopback interface name is lo (not lo0), 135 * we translate lo to lo0, loX to loX. 136 */ 137 if (IFP_IS_LOOP(ifp) && strncmp(if_name(ifp), "lo0", IFNAMSIZ) == 0) 138 return (strlcpy(lxname, "lo", len)); 139 140 /* Short-circuit non ethernet interfaces. */ 141 if (!IFP_IS_ETH(ifp) || use_real_ifnames) 142 return (strlcpy(lxname, if_name(ifp), len)); 143 144 /* Determine the (relative) unit number for ethernet interfaces. */ 145 return (if_foreach(ifname_bsd_to_linux_ifp_cb, &arg)); 146 } 147 148 /* 149 * Translate a Linux interface name to a FreeBSD interface name, 150 * and return the associated ifnet structure 151 * bsdname and lxname need to be least IFNAMSIZ bytes long, but 152 * can point to the same buffer. 153 */ 154 struct ifname_linux_to_ifp_cb_s { 155 bool is_lo; 156 bool is_eth; 157 int ethno; 158 int unit; 159 const char *lxname; 160 if_t ifp; 161 }; 162 163 static int 164 ifname_linux_to_ifp_cb(if_t ifp, void *arg) 165 { 166 struct ifname_linux_to_ifp_cb_s *cbs = arg; 167 168 NET_EPOCH_ASSERT(); 169 170 /* 171 * Allow Linux programs to use FreeBSD names. Don't presume 172 * we never have an interface named "eth", so don't make 173 * the test optional based on is_eth. 174 */ 175 if (strncmp(if_name(ifp), cbs->lxname, LINUX_IFNAMSIZ) == 0) 176 goto out; 177 if (cbs->is_eth && IFP_IS_ETH(ifp) && cbs->unit == cbs->ethno) 178 goto out; 179 if (cbs->is_lo && IFP_IS_LOOP(ifp)) 180 goto out; 181 if (IFP_IS_ETH(ifp)) 182 cbs->ethno++; 183 return (0); 184 185 out: 186 cbs->ifp = ifp; 187 return (1); 188 } 189 190 struct ifnet * 191 ifname_linux_to_ifp(struct thread *td, const char *lxname) 192 { 193 struct ifname_linux_to_ifp_cb_s arg = { 194 .ethno = 0, 195 .lxname = lxname, 196 .ifp = NULL, 197 }; 198 int len; 199 char *ep; 200 201 NET_EPOCH_ASSERT(); 202 203 for (len = 0; len < LINUX_IFNAMSIZ; ++len) 204 if (!isalpha(lxname[len]) || lxname[len] == '\0') 205 break; 206 if (len == 0 || len == LINUX_IFNAMSIZ) 207 return (NULL); 208 /* 209 * Linux loopback interface name is lo (not lo0), 210 * we translate lo to lo0, loX to loX. 211 */ 212 arg.is_lo = (len == 2 && strncmp(lxname, "lo", LINUX_IFNAMSIZ) == 0); 213 arg.unit = (int)strtoul(lxname + len, &ep, 10); 214 if ((ep == NULL || ep == lxname + len || ep >= lxname + LINUX_IFNAMSIZ) && 215 arg.is_lo == 0) 216 return (NULL); 217 arg.is_eth = (len == 3 && strncmp(lxname, "eth", len) == 0); 218 219 if_foreach(ifname_linux_to_ifp_cb, &arg); 220 return (arg.ifp); 221 } 222 223 int 224 ifname_linux_to_bsd(struct thread *td, const char *lxname, char *bsdname) 225 { 226 struct epoch_tracker et; 227 struct ifnet *ifp; 228 229 CURVNET_SET(TD_TO_VNET(td)); 230 NET_EPOCH_ENTER(et); 231 ifp = ifname_linux_to_ifp(td, lxname); 232 if (ifp != NULL && bsdname != NULL) 233 strlcpy(bsdname, if_name(ifp), IFNAMSIZ); 234 NET_EPOCH_EXIT(et); 235 CURVNET_RESTORE(); 236 return (ifp != NULL ? 0 : EINVAL); 237 } 238 239 unsigned short 240 linux_ifflags(struct ifnet *ifp) 241 { 242 unsigned short flags; 243 244 NET_EPOCH_ASSERT(); 245 246 flags = if_getflags(ifp) | if_getdrvflags(ifp); 247 return (bsd_to_linux_ifflags(flags)); 248 } 249 250 unsigned short 251 bsd_to_linux_ifflags(int fl) 252 { 253 unsigned short flags = 0; 254 255 if (fl & IFF_UP) 256 flags |= LINUX_IFF_UP; 257 if (fl & IFF_BROADCAST) 258 flags |= LINUX_IFF_BROADCAST; 259 if (fl & IFF_DEBUG) 260 flags |= LINUX_IFF_DEBUG; 261 if (fl & IFF_LOOPBACK) 262 flags |= LINUX_IFF_LOOPBACK; 263 if (fl & IFF_POINTOPOINT) 264 flags |= LINUX_IFF_POINTOPOINT; 265 if (fl & IFF_DRV_RUNNING) 266 flags |= LINUX_IFF_RUNNING; 267 if (fl & IFF_NOARP) 268 flags |= LINUX_IFF_NOARP; 269 if (fl & IFF_PROMISC) 270 flags |= LINUX_IFF_PROMISC; 271 if (fl & IFF_ALLMULTI) 272 flags |= LINUX_IFF_ALLMULTI; 273 if (fl & IFF_MULTICAST) 274 flags |= LINUX_IFF_MULTICAST; 275 return (flags); 276 } 277 278 static u_int 279 linux_ifhwaddr_cb(void *arg, struct ifaddr *ifa, u_int count) 280 { 281 struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; 282 struct l_sockaddr *lsa = arg; 283 284 if (count > 0) 285 return (0); 286 if (sdl->sdl_type != IFT_ETHER) 287 return (0); 288 bzero(lsa, sizeof(*lsa)); 289 lsa->sa_family = LINUX_ARPHRD_ETHER; 290 bcopy(LLADDR(sdl), lsa->sa_data, LINUX_IFHWADDRLEN); 291 return (1); 292 } 293 294 int 295 linux_ifhwaddr(struct ifnet *ifp, struct l_sockaddr *lsa) 296 { 297 298 NET_EPOCH_ASSERT(); 299 300 if (IFP_IS_LOOP(ifp)) { 301 bzero(lsa, sizeof(*lsa)); 302 lsa->sa_family = LINUX_ARPHRD_LOOPBACK; 303 return (0); 304 } 305 if (!IFP_IS_ETH(ifp)) 306 return (ENOENT); 307 if (if_foreach_addr_type(ifp, AF_LINK, linux_ifhwaddr_cb, lsa) > 0) 308 return (0); 309 return (ENOENT); 310 } 311