Signed-off-by: Charles Myers <[email protected]>
---
 bsd/porting/networking.cc | 178 +++++++++++++++++++++++++++++++++++++++++++++-
 bsd/porting/networking.hh |   8 +++
 bsd/porting/route.cc      |  86 ++++++++++++++++++----
 bsd/sys/netinet/in.cc     |  10 +++
 bsd/sys/netinet/in.h      |   1 +
 loader.cc                 |  76 +++++++++++++++-----
 6 files changed, 327 insertions(+), 32 deletions(-)

diff --git a/bsd/porting/networking.cc b/bsd/porting/networking.cc
index 016106c..e2767a7 100644
--- a/bsd/porting/networking.cc
+++ b/bsd/porting/networking.cc
@@ -17,6 +17,15 @@
 #include <bsd/sys/net/route.h>
 #include <bsd/sys/netinet/in.h>
 #include <bsd/sys/netinet/in_var.h>
+#ifdef INET6
+#include <bsd/sys/netinet6/in6.h>
+#include <bsd/sys/netinet6/in6_var.h>
+#include <bsd/sys/netinet6/nd6.h>
+#include <bsd/sys/netinet6/ip6_var.h>
+
+// FIXME: inet_pton() is from musl which uses different AF_INET6
+#define LINUX_AF_INET6 10
+#endif // INET6
 #include <bsd/sys/sys/socket.h>
 #include <bsd/sys/sys/socketvar.h>
 
@@ -61,6 +70,18 @@ int if_set_mtu(std::string if_name, u16 mtu)
 
 int start_if(std::string if_name, std::string ip_addr, std::string mask_addr)
 {
+    return if_add_addr(if_name, ip_addr, mask_addr);
+}
+
+int stop_if(std::string if_name, std::string ip_addr)
+{
+    std::string mask_addr;
+
+    return if_del_addr(if_name, ip_addr, mask_addr);
+}
+
+int if_add_ipv4_addr(std::string if_name, std::string ip_addr, std::string 
mask_addr)
+{
     int error, success;
     struct bsd_ifreq oldaddr;
     struct in_aliasreq ifra;
@@ -99,7 +120,9 @@ int start_if(std::string if_name, std::string ip_addr, 
std::string mask_addr)
         error = EINVAL;
         goto out;
     }
+    mask->sin_family = AF_INET;
     mask->sin_len = sizeof(struct bsd_sockaddr_in);
+
     broadcast->sin_family      = AF_INET;
     broadcast->sin_len         = sizeof(struct bsd_sockaddr_in);
     broadcast->sin_addr.s_addr = (addr->sin_addr.s_addr &
@@ -117,7 +140,7 @@ out:
     return (error);
 }
 
-int stop_if(std::string if_name, std::string ip_addr)
+int if_del_ipv4_addr(std::string if_name, std::string ip_addr)
 {
     int error, success;
     struct in_aliasreq ifra;
@@ -155,6 +178,157 @@ out:
     return (error);
 }
 
+#ifdef INET6
+
+int if_add_ipv6_addr(std::string if_name, std::string ip_addr, std::string 
netmask)
+{
+    int error, success;
+    struct in6_ifreq oldaddr;
+    struct in6_aliasreq ifra;
+    struct bsd_sockaddr_in6* addr      = &ifra.ifra_addr;
+    struct bsd_sockaddr_in6* mask      = &ifra.ifra_prefixmask;
+    //struct bsd_sockaddr_in6* dst       = &ifra.ifra_dstaddr;
+    struct ifnet* ifp;
+
+    if (if_name.empty() || ip_addr.empty() || netmask.empty()) {
+        return (EINVAL);
+    }
+
+    bzero(&ifra, sizeof(struct in6_aliasreq));
+    ifra.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+    ifra.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+    /* IF Name */
+    strncpy(ifra.ifra_name, if_name.c_str(), IFNAMSIZ);
+    ifp = ifunit_ref(if_name.c_str());
+    if (!ifp) {
+        return (ENOENT);
+    }
+
+    /* IP Address */
+    if ((success = inet_pton(LINUX_AF_INET6, ip_addr.c_str(), 
&addr->sin6_addr)) != 1) {
+        bsd_log(ERR, "Failed converting IPv6 address %s\n", ip_addr.c_str());
+        error = EINVAL;
+        goto out;
+    }
+    addr->sin6_family = AF_INET6;
+    addr->sin6_len = sizeof(struct bsd_sockaddr_in6);
+
+    /* Mask */
+    if (inet_pton(LINUX_AF_INET6, netmask.c_str(), &mask->sin6_addr) != 1) {
+        /* Interpret it as a prefix length */
+        long prefix_len = strtol(netmask.c_str(), NULL, 0);
+        if (prefix_len < 0 || prefix_len > 128) {
+            error = EINVAL;
+            goto out;
+        }
+        in6_prefixlen2mask(&mask->sin6_addr, prefix_len);
+    }
+    mask->sin6_family = AF_INET6;
+    mask->sin6_len = sizeof(struct bsd_sockaddr_in6);
+
+    strncpy(oldaddr.ifr_name, if_name.c_str(), IFNAMSIZ);
+    error = in6_control(NULL, SIOCGIFADDR_IN6, (caddr_t)&oldaddr, ifp, NULL);
+    if (!error) {
+        in6_control(NULL, SIOCDIFADDR_IN6, (caddr_t)&oldaddr, ifp, NULL);
+    }
+    error = in6_control(NULL, SIOCAIFADDR_IN6, (caddr_t)&ifra, ifp, NULL);
+
+out:
+    if_rele(ifp);
+    return (error);
+}
+
+int if_del_ipv6_addr(std::string if_name, std::string ip_addr, std::string 
netmask)
+{
+    int error, success;
+    struct in6_aliasreq ifra;
+    struct bsd_sockaddr_in6* addr      = &ifra.ifra_addr;
+    struct bsd_sockaddr_in6* mask      = &ifra.ifra_prefixmask;
+    struct ifnet* ifp;
+
+    if (if_name.empty() || ip_addr.empty() || netmask.empty())
+        return (EINVAL);
+
+    bzero(&ifra, sizeof(struct in6_aliasreq));
+
+    /* IF Name */
+    strncpy(ifra.ifra_name, if_name.c_str(), IFNAMSIZ);
+    ifp = ifunit_ref(if_name.c_str());
+    if (!ifp) {
+        return (ENOENT);
+    }
+
+    /* IP Address */
+    if ((success = inet_pton(LINUX_AF_INET6, ip_addr.c_str(), 
&addr->sin6_addr)) != 1) {
+        bsd_log(ERR, "Failed converting IPv6 address %s\n", ip_addr.c_str());
+        error = EINVAL;
+        goto out;
+    }
+    addr->sin6_family = AF_INET6;
+    addr->sin6_len = sizeof(struct bsd_sockaddr_in6);
+
+    /* Mask */
+    if (inet_pton(LINUX_AF_INET6, netmask.c_str(), &mask->sin6_addr) != 1) {
+        /* Interpret it as a prefix length */
+        long prefix_len = strtol(netmask.c_str(), NULL, 0);
+        if (prefix_len < 0 || prefix_len > 128) {
+            error = EINVAL;
+            goto out;
+        }
+        in6_prefixlen2mask(&mask->sin6_addr, prefix_len);
+    }
+    mask->sin6_family = AF_INET6;
+    mask->sin6_len = sizeof(struct bsd_sockaddr_in6);
+
+    error = in6_control(NULL, SIOCDIFADDR_IN6, (caddr_t)&ifra, ifp, NULL);
+
+out:
+    if_rele(ifp);
+    return (error);
+}
+
+int set_ipv6_accept_rtadv(bool enable)
+{
+    V_ip6_accept_rtadv = enable ? 1 : 0;
+    return 0;
+}
+
+bool get_ipv6_accept_rtadv(void)
+{
+    return (V_ip6_accept_rtadv != 0);
+}
+
+#endif // INET6
+
+int if_add_addr(std::string if_name, std::string ip_addr, std::string 
mask_addr)
+{
+    struct in_addr v4;
+    if (inet_pton(AF_INET, ip_addr.c_str(), &v4)) {
+        return if_add_ipv4_addr(if_name, ip_addr, mask_addr);
+    }
+#ifdef INET6
+    else {
+        return if_add_ipv6_addr(if_name, ip_addr, mask_addr);
+    }
+#endif
+    return EINVAL;
+}
+
+int if_del_addr(std::string if_name, std::string ip_addr, std::string 
mask_addr)
+{
+    struct in_addr v4;
+    if (inet_pton(AF_INET, ip_addr.c_str(), &v4)) {
+        return if_del_ipv4_addr(if_name, ip_addr);
+    }
+#ifdef INET6
+    else {
+        return if_del_ipv6_addr(if_name, ip_addr, mask_addr);
+    }
+#endif
+    return EINVAL;
+}
+
 int ifup(std::string if_name)
 {
     int error;
@@ -194,4 +368,6 @@ std::string if_ip(std::string if_name) {
     }
     return inet_ntoa(((bsd_sockaddr_in*)&(addr.ifr_addr))->sin_addr);
 }
+
+
 }
diff --git a/bsd/porting/networking.hh b/bsd/porting/networking.hh
index 8d06fe2..e25cb00 100644
--- a/bsd/porting/networking.hh
+++ b/bsd/porting/networking.hh
@@ -22,6 +22,14 @@ namespace osv {
     int stop_if(std::string if_name, std::string ip_addr);
     int ifup(std::string if_name);
     std::string if_ip(std::string if_name);
+
+    int if_add_addr(std::string if_name, std::string ip_addr, std::string 
netmask);
+    int if_del_addr(std::string if_name, std::string ip_addr, std::string 
netmask);
+
+#ifdef INET6
+    int set_ipv6_accept_rtadv(bool enable);
+    bool get_ipv6_accept_rtadv(void);
+#endif
 }
 
 #endif /* __NETWORKING_H__ */
diff --git a/bsd/porting/route.cc b/bsd/porting/route.cc
index b1f9d50..794a0a5 100644
--- a/bsd/porting/route.cc
+++ b/bsd/porting/route.cc
@@ -41,6 +41,10 @@
 #include <bsd/sys/sys/socket.h>
 #include <bsd/sys/sys/socketvar.h>
 #include <bsd/sys/sys/sysctl.h>
+#ifdef INET6
+#include <bsd/sys/netinet6/in6.h>
+#include <bsd/sys/netinet6/in6_var.h>
+#endif
 
 int sysctl_rtsock(SYSCTL_HANDLER_ARGS) ;
 
@@ -172,6 +176,59 @@ static struct mbuf*  osv_route_arp_rtmsg(int if_idx, int 
cmd, const char* ip,
     return (m);
 }
 
+static int osv_sockaddr_from_string(struct bsd_sockaddr_storage *addr, const 
char *str)
+{
+    struct bsd_sockaddr_in *sa4 = (struct bsd_sockaddr_in*)addr;
+    if (inet_pton(AF_INET, str, (void*)&sa4->sin_addr)) { 
+        sa4->sin_len = sizeof(*sa4);
+        sa4->sin_family = AF_INET;
+        sa4->sin_port = 0;
+        return 1;
+    }
+#ifdef INET6
+    struct bsd_sockaddr_in6 *sa6 = (struct bsd_sockaddr_in6*)addr;
+    if (inet_pton(AF_INET6, str, (void*)&sa6->sin6_addr)) {
+        sa6->sin6_len = sizeof(*sa6);
+        sa6->sin6_family = AF_INET6;
+        sa6->sin6_port = 0;
+        sa6->sin6_flowinfo = 0;
+        sa6->sin6_scope_id = 0;
+        return 1;
+    }
+#endif
+    return 0;
+}
+
+static int osv_sockaddr_from_prefix_len(int af, struct bsd_sockaddr_storage 
*addr, int prefix_len)
+{
+    switch(af){
+    case AF_INET:
+        {
+            struct bsd_sockaddr_in *sa4 = (struct bsd_sockaddr_in *)addr;
+            sa4->sin_len = sizeof(*sa4);
+            sa4->sin_family = AF_INET;
+            sa4->sin_port = 0;
+            in_prefixlen2mask(&sa4->sin_addr, prefix_len);
+        }
+        return 1;
+#ifdef INET6
+    case AF_INET6:
+        {
+            struct bsd_sockaddr_in6 *sa6 = (struct bsd_sockaddr_in6 *)addr;
+            sa6->sin6_len = sizeof(*sa6);
+            sa6->sin6_family = AF_INET6;
+            sa6->sin6_port = 0;
+            sa6->sin6_flowinfo = 0;
+            sa6->sin6_scope_id = 0;
+            in6_prefixlen2mask(&sa6->sin6_addr, prefix_len);
+        }
+        return 1;
+#endif
+    default:
+        return 0;
+    }
+}
+
 /* Compose a routing message to be sent on socket */
 static struct mbuf*  osv_route_rtmsg(int cmd, const char* destination,
     const char* gateway, const char* netmask, int flags, gw_type type)
@@ -185,10 +242,10 @@ static struct mbuf*  osv_route_rtmsg(int cmd, const char* 
destination,
     struct bsd_ifaddr *ifa;
     bool is_link = type == gw_type::link;
 
-    /* IPv4: Addresses */
-    struct bsd_sockaddr_in dst;
-    struct bsd_sockaddr_in gw;
-    struct bsd_sockaddr_in mask;
+    /* IP: Addresses */
+    struct bsd_sockaddr_storage dst;
+    struct bsd_sockaddr_storage gw;
+    struct bsd_sockaddr_storage mask;
 
     /* Link: Address*/
     struct bsd_sockaddr_dl sdl;
@@ -215,9 +272,7 @@ static struct mbuf*  osv_route_rtmsg(int cmd, const char* 
destination,
     bzero(&sdl, sizeof(sdl));
     bzero(&mask, sizeof(mask));
 
-    dst.sin_family = AF_INET;
-    dst.sin_len = sizeof(struct bsd_sockaddr_in);
-    inet_aton(destination, &dst.sin_addr);
+    osv_sockaddr_from_string(&dst, destination);
 
     if (is_link) {
         /* Get ifindex from name */
@@ -234,15 +289,20 @@ static struct mbuf*  osv_route_rtmsg(int cmd, const char* 
destination,
         memcpy(ea, IF_LLADDR(ifp), ETHER_ADDR_LEN);
         if_rele(ifp);
     } else {
-        gw.sin_family = AF_INET;
-        gw.sin_len = sizeof(struct bsd_sockaddr_in);
-        inet_aton(gateway, &gw.sin_addr);
+        osv_sockaddr_from_string(&gw, gateway);
     }
 
     if (netmask) {
-        mask.sin_family = AF_INET;
-        mask.sin_len = sizeof(struct bsd_sockaddr_in);
-        inet_aton(netmask, &mask.sin_addr);
+        if (osv_sockaddr_from_string(&mask, netmask) == 0) {
+            // Try parsing it as a prefix length
+            char *p_end = NULL;
+            long prefix_len = strtol(netmask, &p_end, 0);
+            if (p_end == netmask) {
+                 // Bad netmask string.  Probably safer to treat it as a host 
route.
+                 prefix_len = (((struct bsd_sockaddr *)&dst)->sa_family == 
AF_INET6) ? 128 : 32;
+            }
+            osv_sockaddr_from_prefix_len(((struct bsd_sockaddr 
*)&dst)->sa_family, &mask, prefix_len);
+        }
     }
 
     /*
diff --git a/bsd/sys/netinet/in.cc b/bsd/sys/netinet/in.cc
index f7c1934..c0c37cd 100644
--- a/bsd/sys/netinet/in.cc
+++ b/bsd/sys/netinet/in.cc
@@ -1673,3 +1673,13 @@ in_domifdetach(struct ifnet *ifp, void *aux)
        lltable_free(ii->ii_llt);
        free(ii);
 }
+
+void
+in_prefixlen2mask(struct in_addr *maskp, int plen)
+{
+       if (plen == 0)
+               maskp->s_addr = 0;
+       else
+               maskp->s_addr = htonl(0xffffffff << (32 - plen));
+}
+
diff --git a/bsd/sys/netinet/in.h b/bsd/sys/netinet/in.h
index 43291b7..672e613 100644
--- a/bsd/sys/netinet/in.h
+++ b/bsd/sys/netinet/in.h
@@ -470,6 +470,7 @@ const char *inet_ntoa_r(struct in_addr ina, char *buf, 
socklen_t); /* in libkern
 const char *inet_ntop(int, const void *, char *, socklen_t); /* in libkern */
 int     inet_pton(int af, const char *, void *); /* in libkern */
 void    in_ifdetach(struct ifnet *);
+void in_prefixlen2mask(struct in_addr *maskp, int plen);
 __END_DECLS
 
 #define        in_hosteq(s, t) ((s).s_addr == (t).s_addr)
diff --git a/loader.cc b/loader.cc
index 3f88ebd..5e04c83 100644
--- a/loader.cc
+++ b/loader.cc
@@ -5,6 +5,8 @@
  * BSD license as described in the LICENSE file in the top-level directory.
  */
 
+#include <bsd/porting/netport.h>
+
 #include "fs/fs.hh"
 #include <bsd/init.hh>
 #include <bsd/net.hh>
@@ -137,8 +139,8 @@ static bool opt_verbose = false;
 static std::string opt_chdir;
 static bool opt_bootchart = false;
 static std::vector<std::string> opt_ip;
-static std::string opt_defaultgw;
-static std::string opt_nameserver;
+static std::vector<std::string> opt_defaultgw;
+static std::vector<std::string> opt_nameserver;
 static std::string opt_redirect;
 static std::chrono::nanoseconds boot_delay;
 bool opt_assign_net = false;
@@ -177,8 +179,8 @@ void parse_options(int loader_argc, char** loader_argv)
         ("cwd", bpo::value<std::vector<std::string>>(), "set current working 
directory")
         ("bootchart", "perform a test boot measuring a time distribution of 
the various operations\n")
         ("ip", bpo::value<std::vector<std::string>>(), "set static IP on NIC")
-        ("defaultgw", bpo::value<std::string>(), "set default gateway address")
-        ("nameserver", bpo::value<std::string>(), "set nameserver address")
+        ("defaultgw", bpo::value<std::vector<std::string>>(), "set default 
gateway address")
+        ("nameserver", bpo::value<std::vector<std::string>>(), "set nameserver 
address")
         ("delay", bpo::value<float>()->default_value(0), "delay in seconds 
before boot")
         ("redirect", bpo::value<std::string>(), "redirect stdout and stderr to 
file")
         ("disable_rofs_cache", "disable ROFS memory cache")
@@ -287,11 +289,11 @@ void parse_options(int loader_argc, char** loader_argv)
     }
 
     if (vars.count("defaultgw")) {
-        opt_defaultgw = vars["defaultgw"].as<std::string>();
+        opt_defaultgw = vars["defaultgw"].as<std::vector<std::string>>();
     }
 
     if (vars.count("nameserver")) {
-        opt_nameserver = vars["nameserver"].as<std::string>();
+        opt_nameserver = vars["nameserver"].as<std::vector<std::string>>();
     }
 
     if (vars.count("redirect")) {
@@ -365,40 +367,77 @@ void* do_main_thread(void *_main_args)
         }
     }
 
+#ifdef INET6
+    // Enable IPv6 StateLess Address AutoConfiguration (SLAAC)
+    osv::set_ipv6_accept_rtadv(true);
+#endif
+
     bool has_if = false;
     osv::for_each_if([&has_if] (std::string if_name) {
         if (if_name == "lo0")
             return;
 
         has_if = true;
-        // Start DHCP by default and wait for an IP
-        if (osv::start_if(if_name, "0.0.0.0", "255.255.255.0") != 0 ||
-            osv::ifup(if_name) != 0)
+
+        if (osv::ifup(if_name) != 0)
             debug("Could not initialize network interface.\n");
+
+        if (opt_ip.size() == 0) {
+            // Start DHCP by default and wait for an IP
+            if (osv::if_add_addr(if_name, "0.0.0.0", "255.255.255.0") != 0)
+                debug("Could not add 0.0.0.0 IP to interface.\n");
+        }
     });
     if (has_if) {
         if (opt_ip.size() == 0) {
             dhcp_start(true);
         } else {
+            // Add interface IP addresses
             for (auto t : opt_ip) {
                 std::vector<std::string> tmp;
-                boost::split(tmp, t, boost::is_any_of(" ,"), 
boost::token_compress_on);
+                boost::split(tmp, t, boost::is_any_of(" ,/"), 
boost::token_compress_on);
                 if (tmp.size() != 3)
                     abort("incorrect parameter on --ip");
 
-                printf("%s: %s\n",tmp[0].c_str(),tmp[1].c_str());
+                printf("%s: %s %s\n",tmp[0].c_str(),tmp[1].c_str(), 
tmp[2].c_str());
 
-                if (osv::start_if(tmp[0], tmp[1], tmp[2]) != 0)
-                    debug("Could not initialize network interface.\n");
+                if (osv::if_add_addr(tmp[0], tmp[1], tmp[2]) != 0)
+                    debug("Could not add IP address to interface.\n");
             }
+            // Add default gateway routes
+            // One default route is allowed for IPv4 and one for IPv6
             if (opt_defaultgw.size() != 0) {
-                osv_route_add_network("0.0.0.0",
-                                      "0.0.0.0",
-                                      opt_defaultgw.c_str());
+                bool has_defaultgw_v4=false, has_defaultgw_v6=false;
+                for (auto t : opt_defaultgw) {
+                    auto addr = boost::asio::ip::address::from_string(t);
+                    if (addr.is_v4()) {
+                        if (!has_defaultgw_v4) {
+                            osv_route_add_network("0.0.0.0",
+                                                  "0.0.0.0",
+                                                  t.c_str());
+                            has_defaultgw_v4 = true;
+                        }
+                    }
+                    else {
+                        if (!has_defaultgw_v6) {
+                            osv_route_add_network("::",
+                                                  "::",
+                                                  t.c_str());
+                            has_defaultgw_v6 = true;
+                        }
+                    }
+                }
             }
+            // Add nameserver addresses
             if (opt_nameserver.size() != 0) {
-                auto addr = 
boost::asio::ip::address_v4::from_string(opt_nameserver);
-                osv::set_dns_config({addr}, std::vector<std::string>());
+                std::vector<boost::asio::ip::address> dns_servers;
+                for (auto t : opt_nameserver) {
+                    auto addr = boost::asio::ip::address::from_string(t);
+                    dns_servers.push_back(addr);
+                }
+                if (!dns_servers.empty()) {
+                    osv::set_dns_config(dns_servers, 
std::vector<std::string>());
+                }
             }
         }
     }
@@ -465,6 +504,7 @@ void* do_main_thread(void *_main_args)
         for (int i = 0; i < count; i++) {
             if (!strcmp(".", namelist[i]->d_name) ||
                     !strcmp("..", namelist[i]->d_name)) {
+                free(namelist[i]);
                 continue;
             }
             std::string fn("/init/");
-- 
2.7.4

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to