ext/socket/ifaddr.c

Go to the documentation of this file.
00001 #include "rubysocket.h"
00002 
00003 #ifdef HAVE_GETIFADDRS
00004 
00005 /*
00006  * ifa_flags is usually unsigned int.
00007  * However it is uint64_t on SunOS 5.11 (OpenIndiana).
00008  */
00009 #ifdef HAVE_LONG_LONG
00010 typedef unsigned LONG_LONG ifa_flags_t;
00011 #define PRIxIFAFLAGS PRI_LL_PREFIX"x"
00012 #define IFAFLAGS2NUM(flags) ULL2NUM(flags)
00013 #else
00014 typedef unsigned int ifa_flags_t;
00015 #define PRIxIFAFLAGS "x"
00016 #define IFAFLAGS2NUM(flags) UINT2NUM(flags)
00017 #endif
00018 
00019 VALUE rb_cSockIfaddr;
00020 
00021 typedef struct rb_ifaddr_tag rb_ifaddr_t;
00022 typedef struct rb_ifaddr_root_tag rb_ifaddr_root_t;
00023 
00024 struct rb_ifaddr_tag {
00025     int ord;
00026     struct ifaddrs *ifaddr;
00027     rb_ifaddr_root_t *root;
00028 };
00029 
00030 struct rb_ifaddr_root_tag {
00031     int refcount;
00032     int numifaddrs;
00033     rb_ifaddr_t ary[1];
00034 };
00035 
00036 static rb_ifaddr_root_t *
00037 get_root(const rb_ifaddr_t *ifaddr)
00038 {
00039     return (rb_ifaddr_root_t *)((char *)&ifaddr[-ifaddr->ord] -
00040                                 offsetof(rb_ifaddr_root_t, ary));
00041 }
00042 
00043 static void
00044 ifaddr_mark(void *ptr)
00045 {
00046 }
00047 
00048 static void
00049 ifaddr_free(void *ptr)
00050 {
00051     rb_ifaddr_t *ifaddr = ptr;
00052     rb_ifaddr_root_t *root = get_root(ifaddr);
00053     root->refcount--;
00054     if (root->refcount == 0) {
00055         freeifaddrs(root->ary[0].ifaddr);
00056         xfree(root);
00057     }
00058 }
00059 
00060 static size_t
00061 ifaddr_memsize(const void *ptr)
00062 {
00063     const rb_ifaddr_t *ifaddr;
00064     const rb_ifaddr_root_t *root;
00065     if (ptr == NULL)
00066         return 0;
00067     ifaddr = ptr;
00068     root = get_root(ifaddr);
00069     return sizeof(rb_ifaddr_root_t) + (root->numifaddrs - 1) * sizeof(rb_ifaddr_t);
00070 }
00071 
00072 static const rb_data_type_t ifaddr_type = {
00073     "socket/ifaddr",
00074     {ifaddr_mark, ifaddr_free, ifaddr_memsize,},
00075 };
00076 
00077 #define IS_IFADDRS(obj) rb_typeddata_is_kind_of((obj), &ifaddr_type)
00078 static inline rb_ifaddr_t *
00079 check_ifaddr(VALUE self)
00080 {
00081       return rb_check_typeddata(self, &ifaddr_type);
00082 }
00083 
00084 static rb_ifaddr_t *
00085 get_ifaddr(VALUE self)
00086 {
00087     rb_ifaddr_t *rifaddr = check_ifaddr(self);
00088 
00089     if (!rifaddr) {
00090         rb_raise(rb_eTypeError, "uninitialized ifaddr");
00091     }
00092     return rifaddr;
00093 }
00094 
00095 static VALUE
00096 rsock_getifaddrs(void)
00097 {
00098     int ret;
00099     int numifaddrs, i;
00100     struct ifaddrs *ifaddrs, *ifa;
00101     rb_ifaddr_root_t *root;
00102     VALUE result;
00103 
00104     ret = getifaddrs(&ifaddrs);
00105     if (ret == -1)
00106         rb_sys_fail("getifaddrs");
00107 
00108     if (!ifaddrs) {
00109         return rb_ary_new();
00110     }
00111 
00112     numifaddrs = 0;
00113     for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next)
00114         numifaddrs++;
00115 
00116     root = xmalloc(sizeof(rb_ifaddr_root_t) + (numifaddrs-1) * sizeof(rb_ifaddr_t));
00117     root->refcount = root->numifaddrs = numifaddrs;
00118 
00119     ifa = ifaddrs;
00120     for (i = 0; i < numifaddrs; i++) {
00121         root->ary[i].ord = i;
00122         root->ary[i].ifaddr = ifa;
00123         root->ary[i].root = root;
00124         ifa = ifa->ifa_next;
00125     }
00126 
00127     result = rb_ary_new2(numifaddrs);
00128     for (i = 0; i < numifaddrs; i++) {
00129         rb_ary_push(result, TypedData_Wrap_Struct(rb_cSockIfaddr, &ifaddr_type, &root->ary[i]));
00130     }
00131 
00132     return result;
00133 }
00134 
00135 /*
00136  * call-seq:
00137  *   ifaddr.name => string
00138  *
00139  * Returns the interface name of _ifaddr_.
00140  */
00141 
00142 static VALUE
00143 ifaddr_name(VALUE self)
00144 {
00145     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00146     struct ifaddrs *ifa = rifaddr->ifaddr;
00147     return rb_str_new_cstr(ifa->ifa_name);
00148 }
00149 
00150 #ifdef HAVE_IF_NAMETOINDEX
00151 /*
00152  * call-seq:
00153  *   ifaddr.ifindex => integer
00154  *
00155  * Returns the interface index of _ifaddr_.
00156  */
00157 
00158 static VALUE
00159 ifaddr_ifindex(VALUE self)
00160 {
00161     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00162     struct ifaddrs *ifa = rifaddr->ifaddr;
00163     unsigned int ifindex = if_nametoindex(ifa->ifa_name);
00164     if (ifindex == 0) {
00165         rb_raise(rb_eArgError, "invalid interface name: %s", ifa->ifa_name);
00166     }
00167     return UINT2NUM(ifindex);
00168 }
00169 #else
00170 #define ifaddr_ifindex rb_f_notimplement
00171 #endif
00172 
00173 /*
00174  * call-seq:
00175  *   ifaddr.flags => integer
00176  *
00177  * Returns the flags of _ifaddr_.
00178  */
00179 
00180 static VALUE
00181 ifaddr_flags(VALUE self)
00182 {
00183     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00184     struct ifaddrs *ifa = rifaddr->ifaddr;
00185     return IFAFLAGS2NUM(ifa->ifa_flags);
00186 }
00187 
00188 /*
00189  * call-seq:
00190  *   ifaddr.addr => addrinfo
00191  *
00192  * Returns the address of _ifaddr_.
00193  * nil is returned if address is not available in _ifaddr_.
00194  */
00195 
00196 static VALUE
00197 ifaddr_addr(VALUE self)
00198 {
00199     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00200     struct ifaddrs *ifa = rifaddr->ifaddr;
00201     if (ifa->ifa_addr)
00202         return rsock_sockaddr_obj(ifa->ifa_addr, rsock_sockaddr_len(ifa->ifa_addr));
00203     return Qnil;
00204 }
00205 
00206 /*
00207  * call-seq:
00208  *   ifaddr.netmask => addrinfo
00209  *
00210  * Returns the netmask address of _ifaddr_.
00211  * nil is returned if netmask is not available in _ifaddr_.
00212  */
00213 
00214 static VALUE
00215 ifaddr_netmask(VALUE self)
00216 {
00217     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00218     struct ifaddrs *ifa = rifaddr->ifaddr;
00219     if (ifa->ifa_netmask)
00220         return rsock_sockaddr_obj(ifa->ifa_netmask, rsock_sockaddr_len(ifa->ifa_netmask));
00221     return Qnil;
00222 }
00223 
00224 /*
00225  * call-seq:
00226  *   ifaddr.broadaddr => addrinfo
00227  *
00228  * Returns the broadcast address of _ifaddr_.
00229  * nil is returned if the flags doesn't have IFF_BROADCAST.
00230  */
00231 
00232 static VALUE
00233 ifaddr_broadaddr(VALUE self)
00234 {
00235     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00236     struct ifaddrs *ifa = rifaddr->ifaddr;
00237     if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr)
00238         return rsock_sockaddr_obj(ifa->ifa_broadaddr, rsock_sockaddr_len(ifa->ifa_broadaddr));
00239     return Qnil;
00240 }
00241 
00242 /*
00243  * call-seq:
00244  *   ifaddr.dstaddr => addrinfo
00245  *
00246  * Returns the destination address of _ifaddr_.
00247  * nil is returned if the flags doesn't have IFF_POINTOPOINT.
00248  */
00249 
00250 static VALUE
00251 ifaddr_dstaddr(VALUE self)
00252 {
00253     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00254     struct ifaddrs *ifa = rifaddr->ifaddr;
00255     if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr)
00256         return rsock_sockaddr_obj(ifa->ifa_dstaddr, rsock_sockaddr_len(ifa->ifa_dstaddr));
00257     return Qnil;
00258 }
00259 
00260 static void
00261 ifaddr_inspect_flags(ifa_flags_t flags, VALUE result)
00262 {
00263     const char *sep = " ";
00264 #define INSPECT_BIT(bit, name) \
00265     if (flags & (bit)) { rb_str_catf(result, "%s" name, sep); flags &= ~(ifa_flags_t)(bit); sep = ","; }
00266 #ifdef IFF_UP
00267     INSPECT_BIT(IFF_UP, "UP")
00268 #endif
00269 #ifdef IFF_BROADCAST
00270     INSPECT_BIT(IFF_BROADCAST, "BROADCAST")
00271 #endif
00272 #ifdef IFF_DEBUG
00273     INSPECT_BIT(IFF_DEBUG, "DEBUG")
00274 #endif
00275 #ifdef IFF_LOOPBACK
00276     INSPECT_BIT(IFF_LOOPBACK, "LOOPBACK")
00277 #endif
00278 #ifdef IFF_POINTOPOINT
00279     INSPECT_BIT(IFF_POINTOPOINT, "POINTOPOINT")
00280 #endif
00281 #ifdef IFF_RUNNING
00282     INSPECT_BIT(IFF_RUNNING, "RUNNING")
00283 #endif
00284 #ifdef IFF_NOARP
00285     INSPECT_BIT(IFF_NOARP, "NOARP")
00286 #endif
00287 #ifdef IFF_PROMISC
00288     INSPECT_BIT(IFF_PROMISC, "PROMISC")
00289 #endif
00290 #ifdef IFF_NOTRAILERS
00291     INSPECT_BIT(IFF_NOTRAILERS, "NOTRAILERS")
00292 #endif
00293 #ifdef IFF_ALLMULTI
00294     INSPECT_BIT(IFF_ALLMULTI, "ALLMULTI")
00295 #endif
00296 #ifdef IFF_MASTER
00297     INSPECT_BIT(IFF_MASTER, "MASTER")
00298 #endif
00299 #ifdef IFF_SLAVE
00300     INSPECT_BIT(IFF_SLAVE, "SLAVE")
00301 #endif
00302 #ifdef IFF_MULTICAST
00303     INSPECT_BIT(IFF_MULTICAST, "MULTICAST")
00304 #endif
00305 #ifdef IFF_PORTSEL
00306     INSPECT_BIT(IFF_PORTSEL, "PORTSEL")
00307 #endif
00308 #ifdef IFF_AUTOMEDIA
00309     INSPECT_BIT(IFF_AUTOMEDIA, "AUTOMEDIA")
00310 #endif
00311 #ifdef IFF_DYNAMIC
00312     INSPECT_BIT(IFF_DYNAMIC, "DYNAMIC")
00313 #endif
00314 #ifdef IFF_LOWER_UP
00315     INSPECT_BIT(IFF_LOWER_UP, "LOWER_UP")
00316 #endif
00317 #ifdef IFF_DORMANT
00318     INSPECT_BIT(IFF_DORMANT, "DORMANT")
00319 #endif
00320 #ifdef IFF_ECHO
00321     INSPECT_BIT(IFF_ECHO, "ECHO")
00322 #endif
00323 #undef INSPECT_BIT
00324     if (flags) {
00325         rb_str_catf(result, "%s%#"PRIxIFAFLAGS, sep, flags);
00326     }
00327 }
00328 
00329 /*
00330  * call-seq:
00331  *   ifaddr.inspect => string
00332  *
00333  * Returns a string to show contents of _ifaddr_.
00334  */
00335 
00336 static VALUE
00337 ifaddr_inspect(VALUE self)
00338 {
00339     rb_ifaddr_t *rifaddr = get_ifaddr(self);
00340     struct ifaddrs *ifa;
00341     VALUE result;
00342 
00343     ifa = rifaddr->ifaddr;
00344 
00345     result = rb_str_new_cstr("#<");
00346 
00347     rb_str_append(result, rb_class_name(CLASS_OF(self)));
00348     rb_str_cat2(result, " ");
00349     rb_str_cat2(result, ifa->ifa_name);
00350 
00351     if (ifa->ifa_flags)
00352         ifaddr_inspect_flags(ifa->ifa_flags, result);
00353 
00354     if (ifa->ifa_addr) {
00355       rb_str_cat2(result, " ");
00356       rsock_inspect_sockaddr(ifa->ifa_addr,
00357           rsock_sockaddr_len(ifa->ifa_addr),
00358           result);
00359     }
00360     if (ifa->ifa_netmask) {
00361       rb_str_cat2(result, " netmask=");
00362       rsock_inspect_sockaddr(ifa->ifa_netmask,
00363           rsock_sockaddr_len(ifa->ifa_netmask),
00364           result);
00365     }
00366 
00367     if ((ifa->ifa_flags & IFF_BROADCAST) && ifa->ifa_broadaddr) {
00368       rb_str_cat2(result, " broadcast=");
00369       rsock_inspect_sockaddr(ifa->ifa_broadaddr,
00370           rsock_sockaddr_len(ifa->ifa_broadaddr),
00371           result);
00372     }
00373 
00374     if ((ifa->ifa_flags & IFF_POINTOPOINT) && ifa->ifa_dstaddr) {
00375       rb_str_cat2(result, " dstaddr=");
00376       rsock_inspect_sockaddr(ifa->ifa_dstaddr,
00377           rsock_sockaddr_len(ifa->ifa_dstaddr),
00378           result);
00379     }
00380 
00381     rb_str_cat2(result, ">");
00382     return result;
00383 }
00384 #endif
00385 
00386 #ifdef HAVE_GETIFADDRS
00387 /*
00388  * call-seq:
00389  *   Socket.getifaddrs => [ifaddr1, ...]
00390  *
00391  * Returns an array of interface addresses.
00392  * An element of the array is an instance of Socket::Ifaddr.
00393  *
00394  * This method can be used to find multicast-enabled interfaces:
00395  *
00396  *   pp Socket.getifaddrs.reject {|ifaddr|
00397  *     !ifaddr.addr.ip? || (ifaddr.flags & Socket::IFF_MULTICAST == 0)
00398  *   }.map {|ifaddr| [ifaddr.name, ifaddr.ifindex, ifaddr.addr] }
00399  *   #=> [["eth0", 2, #<Addrinfo: 221.186.184.67>],
00400  *   #    ["eth0", 2, #<Addrinfo: fe80::216:3eff:fe95:88bb%eth0>]]
00401  *
00402  * Example result on GNU/Linux:
00403  *   pp Socket.getifaddrs
00404  *   #=> [#<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 PACKET[protocol=0 lo hatype=772 HOST hwaddr=00:00:00:00:00:00]>,
00405  *   #    #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=00:16:3e:95:88:bb] broadcast=PACKET[protocol=0 eth0 hatype=1 HOST hwaddr=ff:ff:ff:ff:ff:ff]>,
00406  *   #    #<Socket::Ifaddr sit0 NOARP PACKET[protocol=0 sit0 hatype=776 HOST hwaddr=00:00:00:00]>,
00407  *   #    #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 127.0.0.1 netmask=255.0.0.0>,
00408  *   #    #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 221.186.184.67 netmask=255.255.255.240 broadcast=221.186.184.79>,
00409  *   #    #<Socket::Ifaddr lo UP,LOOPBACK,RUNNING,0x10000 ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>,
00410  *   #    #<Socket::Ifaddr eth0 UP,BROADCAST,RUNNING,MULTICAST,0x10000 fe80::216:3eff:fe95:88bb%eth0 netmask=ffff:ffff:ffff:ffff::>]
00411  *
00412  * Example result on FreeBSD:
00413  *   pp Socket.getifaddrs
00414  *   #=> [#<Socket::Ifaddr usbus0 UP,0x10000 LINK[usbus0]>,
00415  *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 LINK[re0 3a:d0:40:9a:fe:e8]>,
00416  *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 10.250.10.18 netmask=255.255.255.? (7 bytes for 16 bytes sockaddr_in) broadcast=10.250.10.255>,
00417  *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 fe80:2::38d0:40ff:fe9a:fee8 netmask=ffff:ffff:ffff:ffff::>,
00418  *   #    #<Socket::Ifaddr re0 UP,BROADCAST,RUNNING,MULTICAST,0x800 2001:2e8:408:10::12 netmask=UNSPEC>,
00419  *   #    #<Socket::Ifaddr plip0 POINTOPOINT,MULTICAST,0x800 LINK[plip0]>,
00420  *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST LINK[lo0]>,
00421  *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST ::1 netmask=ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff>,
00422  *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST fe80:4::1 netmask=ffff:ffff:ffff:ffff::>,
00423  *   #    #<Socket::Ifaddr lo0 UP,LOOPBACK,RUNNING,MULTICAST 127.0.0.1 netmask=255.?.?.? (5 bytes for 16 bytes sockaddr_in)>]
00424  *
00425  */
00426 
00427 static VALUE
00428 socket_s_getifaddrs(VALUE self)
00429 {
00430     return rsock_getifaddrs();
00431 }
00432 #else
00433 #define socket_s_getifaddrs rb_f_notimplement
00434 #endif
00435 
00436 void
00437 rsock_init_sockifaddr(void)
00438 {
00439 #ifdef HAVE_GETIFADDRS
00440     /*
00441      * Document-class: Socket::Ifaddr
00442      *
00443      * Socket::Ifaddr represents a result of getifaddrs() function.
00444      */
00445     rb_cSockIfaddr = rb_define_class_under(rb_cSocket, "Ifaddr", rb_cData);
00446     rb_define_method(rb_cSockIfaddr, "inspect", ifaddr_inspect, 0);
00447     rb_define_method(rb_cSockIfaddr, "name", ifaddr_name, 0);
00448     rb_define_method(rb_cSockIfaddr, "ifindex", ifaddr_ifindex, 0);
00449     rb_define_method(rb_cSockIfaddr, "flags", ifaddr_flags, 0);
00450     rb_define_method(rb_cSockIfaddr, "addr", ifaddr_addr, 0);
00451     rb_define_method(rb_cSockIfaddr, "netmask", ifaddr_netmask, 0);
00452     rb_define_method(rb_cSockIfaddr, "broadaddr", ifaddr_broadaddr, 0);
00453     rb_define_method(rb_cSockIfaddr, "dstaddr", ifaddr_dstaddr, 0);
00454 #endif
00455 
00456     rb_define_singleton_method(rb_cSocket, "getifaddrs", socket_s_getifaddrs, 0);
00457 }
00458 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7