1 /*-
2 * Copyright (c) 2013 Antti Kantee <pooka@iki.fi>
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 ``AS IS'' AND ANY EXPRESS
14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * 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 OR
19 * 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/kernel.h>
28 #include <sys/socketvar.h>
29
30 #include <net/if.h>
31 #include <net/if_dl.h>
32 #include <net/if_ether.h>
33 #include <net/if_bridgevar.h>
34 #include <net/if_types.h>
35 #include <net/route.h>
36
37 #include <netinet/in.h>
38 #include <netinet/icmp6.h>
39
40 #include <netinet6/in6.h>
41 #include <netinet6/in6_var.h>
42 #include <netinet6/ip6_var.h>
43 #include <netinet6/nd6.h>
44 #include <netinet6/scope6_var.h>
45
46 #include "rump_private.h"
47
48 #include "netconfig_if_priv.h"
49 #include "netconfig_private.h"
50
51 static struct socket *in4so;
52 static struct socket *in6so;
53 static struct socket *rtso;
54
55 #define CHECKDOMAIN(dom) if (!(dom)) return EAFNOSUPPORT
56
57 static int
wrapifioctl(struct socket * so,u_long cmd,void * data)58 wrapifioctl(struct socket *so, u_long cmd, void *data)
59 {
60 int rv;
61
62 KERNEL_LOCK(1, NULL);
63 rv = ifioctl(so, cmd, data, curlwp);
64 KERNEL_UNLOCK_ONE(NULL);
65
66 return rv;
67 }
68
69 int
rump_netconfig_ifcreate(const char * ifname)70 rump_netconfig_ifcreate(const char *ifname)
71 {
72 struct ifreq ifr;
73
74 memset(&ifr, 0, sizeof(ifr));
75 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
76 return wrapifioctl(in4so, SIOCIFCREATE, &ifr);
77 }
78
79 static void
addup(short * fp)80 addup(short *fp)
81 {
82
83 *fp |= IFF_UP;
84 }
85
86 static void
remup(short * fp)87 remup(short *fp)
88 {
89
90 *fp &= ~IFF_UP;
91 }
92
93 static int
chflag(const char * ifname,void (* edflag)(short *))94 chflag(const char *ifname, void (*edflag)(short *))
95 {
96 struct ifreq ifr;
97 int rv;
98
99 memset(&ifr, 0, sizeof(ifr));
100 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
101 if ((rv = wrapifioctl(in4so, SIOCGIFFLAGS, &ifr)) != 0)
102 return rv;
103 edflag(&ifr.ifr_flags);
104
105 return wrapifioctl(in4so, SIOCSIFFLAGS, &ifr);
106 }
107
108 int
rump_netconfig_ifup(const char * ifname)109 rump_netconfig_ifup(const char *ifname)
110 {
111
112 return chflag(ifname, addup);
113 }
114
115 int
rump_netconfig_ifdown(const char * ifname)116 rump_netconfig_ifdown(const char *ifname)
117 {
118
119 return chflag(ifname, remup);
120 }
121
122 int
rump_netconfig_ifsetlinkstr(const char * ifname,const char * linkstr)123 rump_netconfig_ifsetlinkstr(const char *ifname, const char *linkstr)
124 {
125 struct ifdrv ifd;
126
127 memset(&ifd, 0, sizeof(ifd));
128 strlcpy(ifd.ifd_name, ifname, sizeof(ifd.ifd_name));
129 ifd.ifd_cmd = 0;
130 ifd.ifd_data = __UNCONST(linkstr);
131 ifd.ifd_len = strlen(linkstr)+1;
132
133 return wrapifioctl(in4so, SIOCSLINKSTR, &ifd);
134 }
135
136 int
rump_netconfig_ifdestroy(const char * ifname)137 rump_netconfig_ifdestroy(const char *ifname)
138 {
139 struct ifreq ifr;
140
141 memset(&ifr, 0, sizeof(ifr));
142 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
143 return wrapifioctl(in4so, SIOCIFDESTROY, &ifr);
144 }
145
146 /*
147 * network bridge manipulation (bridge is created with ifbridge)
148 */
149
150 static int
brioctl(const char * bridgename,const char * ifname,unsigned long op)151 brioctl(const char *bridgename, const char *ifname, unsigned long op)
152 {
153 struct ifdrv ifd;
154 struct ifbreq req;
155
156 memset(&req, 0, sizeof(req));
157 strlcpy(req.ifbr_ifsname, ifname, sizeof(req.ifbr_ifsname));
158
159 memset(&ifd, 0, sizeof(ifd));
160 strlcpy(ifd.ifd_name, bridgename, sizeof(ifd.ifd_name));
161 ifd.ifd_cmd = op;
162 ifd.ifd_len = sizeof(req);
163 ifd.ifd_data = &req;
164
165
166 return wrapifioctl(in4so, SIOCSDRVSPEC, &ifd);
167 }
168
169 int
rump_netconfig_bradd(const char * bridgename,const char * ifname)170 rump_netconfig_bradd(const char *bridgename, const char *ifname)
171 {
172
173 return brioctl(bridgename, ifname, BRDGADD);
174 }
175
176 int
rump_netconfig_brdel(const char * bridgename,const char * ifname)177 rump_netconfig_brdel(const char *bridgename, const char *ifname)
178 {
179
180 return brioctl(bridgename, ifname, BRDGDEL);
181 }
182
183 static int
cfg_ipv4(const char * ifname,const char * addr,in_addr_t m_addr)184 cfg_ipv4(const char *ifname, const char *addr, in_addr_t m_addr)
185 {
186 struct ifaliasreq ia;
187 struct sockaddr_in *sin;
188 int rv;
189
190 CHECKDOMAIN(in4so);
191
192 memset(&ia, 0, sizeof(ia));
193 strlcpy(ia.ifra_name, ifname, sizeof(ia.ifra_name));
194
195 sin = (struct sockaddr_in *)&ia.ifra_addr;
196 sin->sin_family = AF_INET;
197 sin->sin_len = sizeof(*sin);
198 sin->sin_addr.s_addr = inet_addr(addr);
199
200 sin = (struct sockaddr_in *)&ia.ifra_mask;
201 sin->sin_family = AF_INET;
202 sin->sin_len = sizeof(*sin);
203 sin->sin_addr.s_addr = m_addr;
204
205 sin = (struct sockaddr_in *)&ia.ifra_broadaddr;
206 sin->sin_family = AF_INET;
207 sin->sin_len = sizeof(*sin);
208 sin->sin_addr.s_addr = inet_addr(addr) | ~m_addr;
209
210 rv = wrapifioctl(in4so, SIOCAIFADDR, &ia);
211 /*
212 * small pause so that we can assume interface is usable when
213 * we return (ARPs have trickled through, etc.)
214 */
215 if (rv == 0)
216 kpause("ramasee", false, mstohz(50), NULL);
217 return rv;
218 }
219
220 int
rump_netconfig_ipv4_ifaddr(const char * ifname,const char * addr,const char * mask)221 rump_netconfig_ipv4_ifaddr(const char *ifname, const char *addr,
222 const char *mask)
223 {
224
225 return cfg_ipv4(ifname, addr, inet_addr(mask));
226 }
227
228 int
rump_netconfig_ipv4_ifaddr_cidr(const char * ifname,const char * addr,int mask)229 rump_netconfig_ipv4_ifaddr_cidr(const char *ifname, const char *addr,
230 int mask)
231 {
232
233 if (mask < 0 || mask > 32)
234 return EINVAL;
235 return cfg_ipv4(ifname, addr, htonl(~0U<<(32-mask)));
236 }
237
238 int
rump_netconfig_ipv6_ifaddr(const char * ifname,const char * addr,int prefixlen)239 rump_netconfig_ipv6_ifaddr(const char *ifname, const char *addr, int prefixlen)
240 {
241 struct sockaddr_in6 *sin6;
242 struct in6_aliasreq ia;
243 int rv;
244
245 CHECKDOMAIN(in6so);
246
247 /* pfft, you do the bitnibbling */
248 if (prefixlen % 8)
249 return EINVAL;
250
251 memset(&ia, 0, sizeof(ia));
252 strlcpy(ia.ifra_name, ifname, sizeof(ia.ifra_name));
253
254 ia.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
255 ia.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
256
257 sin6 = (struct sockaddr_in6 *)&ia.ifra_addr;
258 sin6->sin6_family = AF_INET6;
259 sin6->sin6_len = sizeof(*sin6);
260 netconfig_inet_pton6(addr, &sin6->sin6_addr);
261
262 sin6 = (struct sockaddr_in6 *)&ia.ifra_prefixmask;
263 sin6->sin6_family = AF_INET6;
264 sin6->sin6_len = sizeof(*sin6);
265 memset(&sin6->sin6_addr, 0, sizeof(sin6->sin6_addr));
266 memset(&sin6->sin6_addr, 0xff, prefixlen / 8);
267
268 rv = wrapifioctl(in6so, SIOCAIFADDR_IN6, &ia);
269 /*
270 * small pause so that we can assume interface is usable when
271 * we return (ARPs have trickled through, etc.)
272 */
273 if (rv == 0)
274 kpause("ramasee", false, mstohz(50), NULL);
275 return rv;
276 }
277
278 int
rump_netconfig_ipv4_gw(const char * gwaddr)279 rump_netconfig_ipv4_gw(const char *gwaddr)
280 {
281 struct rt_msghdr rtm, *rtmp;
282 struct sockaddr_in sin;
283 struct mbuf *m;
284 int off, rv;
285
286 CHECKDOMAIN(in4so);
287
288 memset(&rtm, 0, sizeof(rtm));
289 rtm.rtm_type = RTM_ADD;
290 rtm.rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
291 rtm.rtm_version = RTM_VERSION;
292 rtm.rtm_seq = 2;
293 rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
294
295 m = m_gethdr(M_WAIT, MT_DATA);
296 m->m_pkthdr.len = 0;
297 m_copyback(m, 0, sizeof(rtm), &rtm);
298 off = sizeof(rtm);
299
300 /* dest */
301 memset(&sin, 0, sizeof(sin));
302 sin.sin_family = AF_INET;
303 sin.sin_len = sizeof(sin);
304 m_copyback(m, off, sin.sin_len, &sin);
305 RT_ADVANCE(off, (struct sockaddr *)&sin);
306
307 /* gw */
308 sin.sin_addr.s_addr = inet_addr(gwaddr);
309 m_copyback(m, off, sin.sin_len, &sin);
310 RT_ADVANCE(off, (struct sockaddr *)&sin);
311
312 /* mask */
313 sin.sin_addr.s_addr = 0;
314 m_copyback(m, off, sin.sin_len, &sin);
315 RT_ADVANCE(off, (struct sockaddr *)&sin);
316
317 m = m_pullup(m, sizeof(*rtmp));
318 rtmp = mtod(m, struct rt_msghdr *);
319 m->m_pkthdr.len = rtmp->rtm_msglen = off;
320
321 solock(rtso);
322 #if __NetBSD_Prereq__(7,99,26)
323 rv = rtso->so_proto->pr_usrreqs->pr_send(rtso, m, NULL, NULL, curlwp);
324 #else
325 rv = rtso->so_proto->pr_output(m, rtso);
326 #endif
327 sounlock(rtso);
328
329 return rv;
330 }
331
332 int
rump_netconfig_ipv6_gw(const char * gwaddr)333 rump_netconfig_ipv6_gw(const char *gwaddr)
334 {
335 struct rt_msghdr rtm, *rtmp;
336 struct sockaddr_in6 sin6;
337 struct mbuf *m;
338 int off, rv;
339
340 CHECKDOMAIN(in6so);
341
342 memset(&rtm, 0, sizeof(rtm));
343 rtm.rtm_type = RTM_ADD;
344 rtm.rtm_flags = RTF_UP | RTF_STATIC | RTF_GATEWAY;
345 rtm.rtm_version = RTM_VERSION;
346 rtm.rtm_seq = 2;
347 rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
348
349 m = m_gethdr(M_WAIT, MT_DATA);
350 m->m_pkthdr.len = 0;
351 m_copyback(m, 0, sizeof(rtm), &rtm);
352 off = sizeof(rtm);
353
354 /* dest */
355 memset(&sin6, 0, sizeof(sin6));
356 sin6.sin6_family = AF_INET6;
357 sin6.sin6_len = sizeof(sin6);
358 m_copyback(m, off, sin6.sin6_len, &sin6);
359 RT_ADVANCE(off, (struct sockaddr *)&sin6);
360
361 /* gw */
362 netconfig_inet_pton6(gwaddr, &sin6.sin6_addr);
363 m_copyback(m, off, sin6.sin6_len, &sin6);
364 RT_ADVANCE(off, (struct sockaddr *)&sin6);
365
366 /* mask */
367 memset(&sin6.sin6_addr, 0, sizeof(sin6.sin6_addr));
368 m_copyback(m, off, sin6.sin6_len, &sin6);
369 off = m->m_pkthdr.len;
370
371 m = m_pullup(m, sizeof(*rtmp));
372 rtmp = mtod(m, struct rt_msghdr *);
373 rtmp->rtm_msglen = off;
374
375 solock(rtso);
376 #if __NetBSD_Prereq__(7,99,26)
377 rv = rtso->so_proto->pr_usrreqs->pr_send(rtso, m, NULL, NULL, curlwp);
378 #else
379 rv = rtso->so_proto->pr_output(m, rtso);
380 #endif
381 sounlock(rtso);
382
383 return rv;
384 }
385
386 /* Perform IPv6 autoconfiguration for the specified interface.
387 * This function sets the kernel to accept IPv6 RAs on all interfaces,
388 * brings the interface up and sends a single IPv6 RS packet to the
389 * all-routers multicast address from the specified interface. No attempt is
390 * made to check whether or not this actually provoked an RA in response.
391 */
392
393 int
rump_netconfig_auto_ipv6(const char * ifname)394 rump_netconfig_auto_ipv6(const char *ifname)
395 {
396 struct ifnet *ifp;
397 int ifindex;
398 struct socket *rsso = NULL;
399 int rv = 0;
400 int hoplimit = 255;
401 struct mbuf *m_nam = NULL,
402 *m_outbuf = NULL;
403 struct sockaddr_in6 *sin6;
404 char *buf;
405 struct nd_router_solicit rs;
406 struct nd_opt_hdr opt;
407
408 ifp = ifunit(ifname);
409 if (ifp == NULL) {
410 rv = ENXIO;
411 goto out;
412 }
413 if (ifp->if_sadl->sdl_type != IFT_ETHER) {
414 rv = EINVAL;
415 goto out;
416 }
417
418 rv = socreate(PF_INET6, &rsso, SOCK_RAW, IPPROTO_ICMPV6, curlwp, NULL);
419 if (rv != 0)
420 goto out;
421 ifindex = ifp->if_index;
422 rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_IF,
423 &ifindex, sizeof ifindex);
424 if (rv != 0)
425 goto out;
426 rv = so_setsockopt(curlwp, rsso, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
427 &hoplimit, sizeof hoplimit);
428 if (rv != 0)
429 goto out;
430
431 m_nam = m_get(M_WAIT, MT_SONAME);
432 sin6 = mtod(m_nam, struct sockaddr_in6 *);
433 sin6->sin6_len = m_nam->m_len = sizeof (*sin6);
434 sin6->sin6_family = AF_INET6;
435 netconfig_inet_pton6("ff02::2", &sin6->sin6_addr);
436
437 #define rslen (sizeof rs + sizeof opt + ETHER_ADDR_LEN)
438 CTASSERT(rslen <= MCLBYTES);
439 m_outbuf = m_gethdr(M_WAIT, MT_DATA);
440 m_clget(m_outbuf, M_WAIT);
441 m_outbuf->m_pkthdr.len = m_outbuf->m_len = rslen;
442
443
444 #if __NetBSD_Prereq__(7,99,31)
445 m_set_rcvif(m_outbuf, NULL);
446 #else
447 m_outbuf->m_pkthdr.rcvif = NULL;
448 #endif
449
450 #undef rslen
451 buf = mtod(m_outbuf, char *);
452 memset(&rs, 0, sizeof rs);
453 rs.nd_rs_type = ND_ROUTER_SOLICIT;
454 memset(&opt, 0, sizeof opt);
455 opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR;
456 opt.nd_opt_len = 1; /* units of 8 octets */
457 memcpy(buf, &rs, sizeof rs);
458 buf += sizeof rs;
459 memcpy(buf, &opt, sizeof opt);
460 buf += sizeof opt;
461 memcpy(buf, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN);
462
463 ip6_accept_rtadv = 1;
464 rv = rump_netconfig_ifup(ifname);
465 if (rv != 0)
466 goto out;
467 #if __NetBSD_Prereq__(7,99,12)
468 rv = (*rsso->so_send)(rsso, (struct sockaddr *)sin6, NULL, m_outbuf,
469 NULL, 0, curlwp);
470 #else
471 rv = (*rsso->so_send)(rsso, m_nam, NULL, m_outbuf, NULL, 0, curlwp);
472 #endif
473 if (rv == 0)
474 /* *(so_send)() takes ownership of m_outbuf on success */
475 m_outbuf = NULL;
476 else
477 goto out;
478
479 rv = 0;
480 out:
481 if (m_nam)
482 m_freem(m_nam);
483 if (m_outbuf)
484 m_freem(m_outbuf);
485 if (rsso)
486 soclose(rsso);
487 return rv;
488 }
489
RUMP_COMPONENT(RUMP_COMPONENT_NET_IFCFG)490 RUMP_COMPONENT(RUMP_COMPONENT_NET_IFCFG)
491 {
492 int rv;
493
494 socreate(PF_INET, &in4so, SOCK_DGRAM, 0, curlwp, NULL);
495 socreate(PF_INET6, &in6so, SOCK_DGRAM, 0, curlwp, NULL);
496
497 if (!in4so && !in6so)
498 panic("netconfig: missing both inet and inet6");
499 if ((rv = socreate(PF_ROUTE, &rtso, SOCK_RAW, 0, curlwp, NULL)) != 0)
500 panic("netconfig socreate route: %d", rv);
501 }
502