Linux.c (5339B)
1 #include <u.h> 2 #include <sys/ioctl.h> 3 #include <sys/socket.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <asm/types.h> 7 #include <net/if_arp.h> 8 #include <linux/netlink.h> 9 #include <linux/rtnetlink.h> 10 #include <net/if.h> 11 #include <libc.h> 12 #include <ip.h> 13 14 /* 15 * Use netlink sockets to find interfaces. 16 * Thanks to Erik Quanstrom. 17 */ 18 static int 19 netlinkrequest(int fd, int type, int (*fn)(struct nlmsghdr *h, Ipifc**, int), 20 Ipifc **ifc, int index) 21 { 22 char buf[1024]; 23 int n; 24 struct sockaddr_nl nl; 25 struct { 26 struct nlmsghdr nlh; 27 struct rtgenmsg g; 28 } req; 29 struct nlmsghdr *h; 30 31 memset(&nl, 0, sizeof nl); 32 nl.nl_family = AF_NETLINK; 33 34 memset(&req, 0, sizeof req); 35 req.nlh.nlmsg_len = sizeof req; 36 req.nlh.nlmsg_type = type; 37 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; 38 req.nlh.nlmsg_pid = 0; 39 req.nlh.nlmsg_seq = 1; 40 req.g.rtgen_family = AF_NETLINK; 41 42 if(sendto(fd, (void*)&req, sizeof req, 0, (struct sockaddr*)&nl, sizeof nl) < 0) 43 return -1; 44 45 while((n=read(fd, buf, sizeof buf)) > 0){ 46 for(h=(struct nlmsghdr*)buf; NLMSG_OK(h, n); h = NLMSG_NEXT(h, n)){ 47 if(h->nlmsg_type == NLMSG_DONE) 48 return 0; 49 if(h->nlmsg_type == NLMSG_ERROR){ 50 werrstr("netlink error"); 51 return -1; 52 } 53 if(fn(h, ifc, index) < 0) 54 return -1; 55 } 56 } 57 werrstr("netlink error"); 58 return -1; 59 } 60 61 static int 62 devsocket(void) 63 { 64 /* we couldn't care less which one; just want to talk to the kernel! */ 65 static int dumb[3] = { AF_INET, AF_PACKET, AF_INET6 }; 66 int i, fd; 67 68 for(i=0; i<nelem(dumb); i++) 69 if((fd = socket(dumb[i], SOCK_DGRAM, 0)) >= 0) 70 return fd; 71 return -1; 72 } 73 74 static int 75 parsertattr(struct rtattr **dst, int ndst, struct nlmsghdr *h, int type, int skip) 76 { 77 struct rtattr *src; 78 int len; 79 80 len = h->nlmsg_len - NLMSG_LENGTH(skip); 81 if(len < 0 || h->nlmsg_type != type){ 82 werrstr("attrs too short"); 83 return -1; 84 } 85 src = (struct rtattr*)((char*)NLMSG_DATA(h) + NLMSG_ALIGN(skip)); 86 87 memset(dst, 0, ndst*sizeof dst[0]); 88 for(; RTA_OK(src, len); src = RTA_NEXT(src, len)) 89 if(src->rta_type < ndst) 90 dst[src->rta_type] = src; 91 return 0; 92 } 93 94 static void 95 rta2ip(int af, uchar *ip, struct rtattr *rta) 96 { 97 memset(ip, 0, IPaddrlen); 98 99 switch(af){ 100 case AF_INET: 101 memmove(ip, v4prefix, IPv4off); 102 memmove(ip+IPv4off, RTA_DATA(rta), IPv4addrlen); 103 break; 104 case AF_INET6: 105 memmove(ip, RTA_DATA(rta), IPaddrlen); 106 break; 107 } 108 } 109 110 static int 111 getlink(struct nlmsghdr *h, Ipifc **ipifclist, int index) 112 { 113 char *p; 114 int fd; 115 struct rtattr *attr[IFLA_MAX+1]; 116 struct ifinfomsg *ifi; 117 Ipifc *ifc; 118 119 ifi = (struct ifinfomsg*)NLMSG_DATA(h); 120 if(index >= 0 && ifi->ifi_index != index) 121 return 0; 122 123 ifc = mallocz(sizeof *ifc, 1); 124 if(ifc == nil) 125 return -1; 126 ifc->index = ifi->ifi_index; 127 128 while(*ipifclist) 129 ipifclist = &(*ipifclist)->next; 130 *ipifclist = ifc; 131 132 if(parsertattr(attr, nelem(attr), h, RTM_NEWLINK, sizeof(struct ifinfomsg)) < 0) 133 return -1; 134 135 if(attr[IFLA_IFNAME]) 136 p = (char*)RTA_DATA(attr[IFLA_IFNAME]); 137 else 138 p = "nil"; 139 strecpy(ifc->dev, ifc->dev+sizeof ifc->dev, p); 140 141 if(attr[IFLA_MTU]) 142 ifc->mtu = *(int*)RTA_DATA(attr[IFLA_MTU]); 143 144 /* 145 * Does not work on old Linux systems, 146 * and not really necessary for my purposes. 147 * Uncomment if you want it bad. 148 * 149 if(attr[IFLA_STATS]){ 150 struct rtnl_link_stats *s; 151 152 s = RTA_DATA(attr[IFLA_STATS]); 153 ifc->pktin = s->rx_packets; 154 ifc->pktout = s->tx_packets; 155 ifc->errin = s->rx_errors; 156 ifc->errout = s->tx_errors; 157 } 158 * 159 */ 160 161 if((fd = devsocket()) > 0){ 162 struct ifreq ifr; 163 164 memset(&ifr, 0, sizeof ifr); 165 strncpy(ifr.ifr_name, p, IFNAMSIZ); 166 ifr.ifr_mtu = 0; 167 if(ioctl(fd, SIOCGIFMTU, &ifr) >= 0) 168 ifc->rp.linkmtu = ifr.ifr_mtu; 169 170 memset(&ifr, 0, sizeof ifr); 171 strncpy(ifr.ifr_name, p, IFNAMSIZ); 172 if(ioctl(fd, SIOCGIFHWADDR, &ifr) >= 0 173 && ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER) 174 memmove(ifc->ether, ifr.ifr_hwaddr.sa_data, 6); 175 176 close(fd); 177 } 178 return 0; 179 } 180 181 static int 182 getaddr(struct nlmsghdr *h, Ipifc **ipifclist, int index) 183 { 184 int mask; 185 Ipifc *ifc; 186 Iplifc *lifc, **l; 187 struct ifaddrmsg *ifa; 188 struct rtattr *attr[IFA_MAX+1]; 189 190 USED(index); 191 192 ifa = (struct ifaddrmsg*)NLMSG_DATA(h); 193 for(ifc=*ipifclist; ifc; ifc=ifc->next) 194 if(ifc->index == ifa->ifa_index) 195 break; 196 if(ifc == nil) 197 return 0; 198 if(parsertattr(attr, nelem(attr), h, RTM_NEWADDR, sizeof(struct ifaddrmsg)) < 0) 199 return -1; 200 201 lifc = mallocz(sizeof *lifc, 1); 202 if(lifc == nil) 203 return -1; 204 for(l=&ifc->lifc; *l; l=&(*l)->next) 205 ; 206 *l = lifc; 207 208 if(attr[IFA_ADDRESS] == nil) 209 attr[IFA_ADDRESS] = attr[IFA_LOCAL]; 210 if(attr[IFA_ADDRESS] == nil) 211 return 0; 212 213 rta2ip(ifa->ifa_family, lifc->ip, attr[IFA_ADDRESS]); 214 215 mask = ifa->ifa_prefixlen/8; 216 if(ifa->ifa_family == AF_INET) 217 mask += IPv4off; 218 memset(lifc->mask, 0xFF, mask); 219 memmove(lifc->net, lifc->ip, mask); 220 221 if(attr[IFA_CACHEINFO]){ 222 struct ifa_cacheinfo *ci; 223 224 ci = RTA_DATA(attr[IFA_CACHEINFO]); 225 lifc->preflt = ci->ifa_prefered; 226 lifc->validlt = ci->ifa_valid; 227 } 228 return 0; 229 } 230 231 Ipifc* 232 readipifc(char *net, Ipifc *ifc, int index) 233 { 234 int fd; 235 236 USED(net); 237 freeipifc(ifc); 238 ifc = nil; 239 240 fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 241 if(fd < 0) 242 return nil; 243 ifc = nil; 244 if(netlinkrequest(fd, RTM_GETLINK, getlink, &ifc, index) < 0 245 || netlinkrequest(fd, RTM_GETADDR, getaddr, &ifc, index) < 0){ 246 close(fd); 247 freeipifc(ifc); 248 return nil; 249 } 250 close(fd); 251 return ifc; 252 }