nixio: Fix interface data for layer 3+ interfaces
[project/luci.git] / libs / nixio / src / address.c
1 /*
2  * nixio - Linux I/O library for lua
3  *
4  *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License.
17  */
18
19 #include "nixio.h"
20 #include <sys/types.h>
21 #include <sys/param.h>
22 #include <errno.h>
23 #include <string.h>
24
25 #ifdef __linux__
26 #include <linux/netdevice.h>
27
28 /* struct net_device_stats is buggy on amd64, redefine it */
29 struct nixio__nds {
30      uint32_t rx_packets;
31      uint32_t tx_packets;
32      uint32_t rx_bytes;
33      uint32_t tx_bytes;
34      uint32_t rx_errors;
35      uint32_t tx_errors;
36      uint32_t rx_dropped;
37      uint32_t tx_dropped;
38      uint32_t multicast;
39      uint32_t collisions;
40
41      uint32_t rx_length_errors;
42      uint32_t rx_over_errors;
43      uint32_t rx_crc_errors;
44      uint32_t rx_frame_errors;
45      uint32_t rx_fifo_errors;
46      uint32_t rx_missed_errors;
47
48      uint32_t tx_aborted_errors;
49      uint32_t tx_carrier_errors;
50      uint32_t tx_fifo_errors;
51      uint32_t tx_heartbeat_errors;
52      uint32_t tx_window_errors;
53
54      uint32_t rx_compressed;
55      uint32_t tx_compressed;
56 };
57 #endif
58
59 #ifndef NI_MAXHOST
60 #define NI_MAXHOST 1025
61 #endif
62
63 /**
64  * address pushing helper
65  */
66 int nixio__addr_parse(nixio_addr *addr, struct sockaddr *saddr) {
67         void *baddr;
68
69         addr->family = saddr->sa_family;
70         if (saddr->sa_family == AF_INET) {
71                 struct sockaddr_in *inetaddr = (struct sockaddr_in*)saddr;
72                 addr->port = ntohs(inetaddr->sin_port);
73                 baddr = &inetaddr->sin_addr;
74         } else if (saddr->sa_family == AF_INET6) {
75                 struct sockaddr_in6 *inet6addr = (struct sockaddr_in6*)saddr;
76                 addr->port = ntohs(inet6addr->sin6_port);
77                 baddr = &inet6addr->sin6_addr;
78 #ifdef AF_PACKET
79         } else if (saddr->sa_family == AF_PACKET) {
80                 struct sockaddr_ll *etheradddr = (struct sockaddr_ll*)saddr;
81                 addr->prefix = etheradddr->sll_hatype;
82                 addr->port = etheradddr->sll_ifindex;
83                 char *c = addr->host;
84                 for (size_t i = 0; i < etheradddr->sll_halen; i++) {
85                         *c++ = nixio__bin2hex[(etheradddr->sll_addr[i] & 0xf0) >> 4];
86                         *c++ = nixio__bin2hex[(etheradddr->sll_addr[i] & 0x0f)];
87                         *c++ = ':';
88                 }
89                 *(c-1) = 0;
90                 return 0;
91 #endif
92         } else {
93                 errno = EAFNOSUPPORT;
94                 return -1;
95         }
96
97         if (!inet_ntop(saddr->sa_family, baddr, addr->host, sizeof(addr->host))) {
98                 return -1;
99         }
100
101         return 0;
102 }
103
104 /**
105  * address pulling helper
106  */
107 int nixio__addr_write(nixio_addr *addr, struct sockaddr *saddr) {
108         if (addr->family == AF_UNSPEC) {
109                 if (strchr(addr->host, ':')) {
110                         addr->family = AF_INET6;
111                 } else {
112                         addr->family = AF_INET;
113                 }
114         }
115         if (addr->family == AF_INET) {
116                 struct sockaddr_in *inetaddr = (struct sockaddr_in *)saddr;
117                 memset(inetaddr, 0, sizeof(struct sockaddr_in));
118
119                 if (inet_pton(AF_INET, addr->host, &inetaddr->sin_addr) < 1) {
120                         return -1;
121                 }
122
123                 inetaddr->sin_family = AF_INET;
124                 inetaddr->sin_port = htons((uint16_t)addr->port);
125                 return 0;
126         } else if (addr->family == AF_INET6) {
127                 struct sockaddr_in6 *inet6addr = (struct sockaddr_in6 *)saddr;
128                 memset(inet6addr, 0, sizeof(struct sockaddr_in6));
129
130                 if (inet_pton(AF_INET6, addr->host, &inet6addr->sin6_addr) < 1) {
131                         return -1;
132                 }
133
134                 inet6addr->sin6_family = AF_INET6;
135                 inet6addr->sin6_port = htons((uint16_t)addr->port);
136                 return 0;
137         } else {
138                 errno = EAFNOSUPPORT;
139                 return -1;
140         }
141 }
142
143 /**
144  * netmask to prefix helper
145  */
146 int nixio__addr_prefix(struct sockaddr *saddr) {
147         int prefix = 0;
148         size_t len;
149         uint8_t *addr;
150
151         if (saddr->sa_family == AF_INET) {
152                 addr = (uint8_t*)(&((struct sockaddr_in*)saddr)->sin_addr);
153                 len = 4;
154         } else if (saddr->sa_family == AF_INET6) {
155                 addr = (uint8_t*)(&((struct sockaddr_in6*)saddr)->sin6_addr);
156                 len = 16;
157         } else {
158                 errno = EAFNOSUPPORT;
159                 return -1;
160         }
161
162         for (size_t i = 0; i < len; i++) {
163                 if (addr[i] == 0xff) {
164                         prefix += 8;
165                 } else if (addr[i] == 0x00) {
166                         break;
167                 } else {
168                         for (uint8_t c = addr[i]; c; c <<= 1) {
169                                 prefix++;
170                         }
171                 }
172         }
173
174         return prefix;
175 }
176
177 /**
178  * getaddrinfo(host, family, port)
179  */
180 static int nixio_getaddrinfo(lua_State *L) {
181         const char *host = NULL;
182         if (!lua_isnoneornil(L, 1)) {
183                 host = luaL_checklstring(L, 1, NULL);
184         }
185         const char *family = luaL_optlstring(L, 2, "any", NULL);
186         const char *port = lua_tolstring(L, 3, NULL);
187
188         struct addrinfo hints, *result, *rp;
189         memset(&hints, 0, sizeof(hints));
190
191         if (!strcmp(family, "any")) {
192                 hints.ai_family = AF_UNSPEC;
193         } else if (!strcmp(family, "inet")) {
194                 hints.ai_family = AF_INET;
195         } else if (!strcmp(family, "inet6")) {
196                 hints.ai_family = AF_INET6;
197         } else {
198                 return luaL_argerror(L, 2, "supported values: any, inet, inet6");
199         }
200
201         hints.ai_socktype = 0;
202         hints.ai_protocol = 0;
203
204         int aistat = getaddrinfo(host, port, &hints, &result);
205         if (aistat) {
206                 lua_pushnil(L);
207                 lua_pushinteger(L, aistat);
208                 lua_pushstring(L, gai_strerror(aistat));
209                 return 3;
210         }
211
212         /* create socket object */
213         lua_newtable(L);
214         int i = 1;
215
216         for (rp = result; rp != NULL; rp = rp->ai_next) {
217                 /* avoid duplicate results */
218 #ifndef __WINNT__
219                 if (!port && rp->ai_socktype != SOCK_STREAM) {
220                         continue;
221                 }
222 #endif
223
224                 if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) {
225                         lua_createtable(L, 0, port ? 4 : 2);
226                         if (rp->ai_family == AF_INET) {
227                                 lua_pushliteral(L, "inet");
228                         } else if (rp->ai_family == AF_INET6) {
229                                 lua_pushliteral(L, "inet6");
230                         }
231                         lua_setfield(L, -2, "family");
232
233                         if (port) {
234                                 switch (rp->ai_socktype) {
235                                         case SOCK_STREAM:
236                                                 lua_pushliteral(L, "stream");
237                                                 break;
238                                         case SOCK_DGRAM:
239                                                 lua_pushliteral(L, "dgram");
240                                                 break;
241                                         case SOCK_RAW:
242                                                 lua_pushliteral(L, "raw");
243                                                 break;
244                                         default:
245                                                 lua_pushnil(L);
246                                                 break;
247                                 }
248                                 lua_setfield(L, -2, "socktype");
249                         }
250
251                         nixio_addr addr;
252                         if (nixio__addr_parse(&addr, rp->ai_addr)) {
253                                 freeaddrinfo(result);
254                                 return nixio__perror_s(L);
255                         }
256
257                         if (port) {
258                                 lua_pushinteger(L, addr.port);
259                                 lua_setfield(L, -2, "port");
260                         }
261
262                         lua_pushstring(L, addr.host);
263                         lua_setfield(L, -2, "address");
264                         lua_rawseti(L, -2, i++);
265                 }
266         }
267
268         freeaddrinfo(result);
269
270         return 1;
271 }
272
273 /**
274  * getnameinfo(address, family)
275  */
276 static int nixio_getnameinfo(lua_State *L) {
277         const char *ip = luaL_checkstring(L, 1);
278         const char *family = luaL_optstring(L, 2, NULL);
279         char host[NI_MAXHOST];
280
281         struct sockaddr_storage saddr;
282         nixio_addr addr;
283         memset(&addr, 0, sizeof(addr));
284         strncpy(addr.host, ip, sizeof(addr.host) - 1);
285
286         if (!family) {
287                 addr.family = AF_UNSPEC;
288         } else if (!strcmp(family, "inet")) {
289                 addr.family = AF_INET;
290         } else if (!strcmp(family, "inet6")) {
291                 addr.family = AF_INET6;
292         } else {
293                 return luaL_argerror(L, 2, "supported values: inet, inet6");
294         }
295
296         nixio__addr_write(&addr, (struct sockaddr *)&saddr);
297
298         int res = getnameinfo((struct sockaddr *)&saddr, sizeof(saddr),
299          host, sizeof(host), NULL, 0, NI_NAMEREQD);
300         if (res) {
301                 lua_pushnil(L);
302                 lua_pushinteger(L, res);
303                 lua_pushstring(L, gai_strerror(res));
304                 return 3;
305         } else {
306                 lua_pushstring(L, host);
307                 return 1;
308         }
309 }
310
311 /**
312  * getsockname()
313  */
314 static int nixio_sock_getsockname(lua_State *L) {
315         int sockfd = nixio__checksockfd(L);
316         struct sockaddr_storage saddr;
317         socklen_t addrlen = sizeof(saddr);
318         nixio_addr addr;
319
320         if (getsockname(sockfd, (struct sockaddr*)&saddr, &addrlen) ||
321          nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) {
322                 return nixio__perror_s(L);
323         }
324
325         lua_pushstring(L, addr.host);
326         lua_pushinteger(L, addr.port);
327         return 2;
328 }
329
330 /**
331  * getpeername()
332  */
333 static int nixio_sock_getpeername(lua_State *L) {
334         int sockfd = nixio__checksockfd(L);
335         struct sockaddr_storage saddr;
336         socklen_t addrlen = sizeof(saddr);
337         nixio_addr addr;
338
339         if (getpeername(sockfd, (struct sockaddr*)&saddr, &addrlen) ||
340          nixio__addr_parse(&addr, (struct sockaddr*)&saddr)) {
341                 return nixio__perror_s(L);
342         }
343
344         lua_pushstring(L, addr.host);
345         lua_pushinteger(L, addr.port);
346         return 2;
347 }
348
349 #if defined(__linux__) || defined(BSD)
350 #ifdef BSD
351 #include <net/if.h>
352 #endif
353 #include <ifaddrs.h>
354
355 static int nixio_getifaddrs(lua_State *L) {
356         nixio_addr addr;
357         struct ifaddrs *ifaddr, *c;
358         if (getifaddrs(&ifaddr) == -1) {
359                 return nixio__perror(L);
360         }
361
362         lua_newtable(L);
363         unsigned int i = 1;
364
365         for (c = ifaddr; c; c = c->ifa_next) {
366                 lua_newtable(L);
367
368                 lua_pushstring(L, c->ifa_name);
369                 lua_setfield(L, -2, "name");
370
371                 lua_createtable(L, 0, 7);
372                         lua_pushboolean(L, c->ifa_flags & IFF_UP);
373                         lua_setfield(L, -2, "up");
374
375                         lua_pushboolean(L, c->ifa_flags & IFF_BROADCAST);
376                         lua_setfield(L, -2, "broadcast");
377
378                         lua_pushboolean(L, c->ifa_flags & IFF_LOOPBACK);
379                         lua_setfield(L, -2, "loopback");
380
381                         lua_pushboolean(L, c->ifa_flags & IFF_POINTOPOINT);
382                         lua_setfield(L, -2, "pointtopoint");
383
384                         lua_pushboolean(L, c->ifa_flags & IFF_NOARP);
385                         lua_setfield(L, -2, "noarp");
386
387                         lua_pushboolean(L, c->ifa_flags & IFF_PROMISC);
388                         lua_setfield(L, -2, "promisc");
389
390                         lua_pushboolean(L, c->ifa_flags & IFF_MULTICAST);
391                         lua_setfield(L, -2, "multicast");
392                 lua_setfield(L, -2, "flags");
393
394                 if (c->ifa_addr) {
395                         if (!nixio__addr_parse(&addr, c->ifa_addr)) {
396                                 lua_pushstring(L, addr.host);
397                                 lua_setfield(L, -2, "addr");
398                         }
399
400                         if (c->ifa_addr->sa_family == AF_INET) {
401                                 lua_pushliteral(L, "inet");
402                         } else if (c->ifa_addr->sa_family == AF_INET6) {
403                                 lua_pushliteral(L, "inet6");
404 #ifdef AF_PACKET
405                         } else if (c->ifa_addr->sa_family == AF_PACKET) {
406                                 lua_pushliteral(L, "packet");
407 #endif
408                         } else {
409                                 lua_pushliteral(L, "unknown");
410                         }
411                         lua_setfield(L, -2, "family");
412
413 #ifdef __linux__
414                         if (c->ifa_addr->sa_family == AF_PACKET) {
415                                 lua_pushinteger(L, addr.port);
416                                 lua_setfield(L, -2, "ifindex");
417
418                                 lua_pushinteger(L, addr.prefix);
419                                 lua_setfield(L, -2, "hatype");
420                         }
421 #endif
422                 }
423
424 #ifdef __linux__
425                 if (c->ifa_data && (!c->ifa_addr
426                                                         || c->ifa_addr->sa_family == AF_PACKET)) {
427                         if (!c->ifa_addr) {
428                                 lua_pushliteral(L, "packet");
429                                 lua_setfield(L, -2, "family");
430                         }
431
432                         lua_createtable(L, 0, 10);
433                         struct nixio__nds *stats = c->ifa_data;
434
435                         lua_pushnumber(L, stats->rx_packets);
436                         lua_setfield(L, -2, "rx_packets");
437
438                         lua_pushnumber(L, stats->tx_packets);
439                         lua_setfield(L, -2, "tx_packets");
440
441                         lua_pushnumber(L, stats->rx_bytes);
442                         lua_setfield(L, -2, "rx_bytes");
443
444                         lua_pushnumber(L, stats->tx_bytes);
445                         lua_setfield(L, -2, "tx_bytes");
446
447                         lua_pushnumber(L, stats->rx_errors);
448                         lua_setfield(L, -2, "rx_errors");
449
450                         lua_pushnumber(L, stats->tx_errors);
451                         lua_setfield(L, -2, "tx_errors");
452
453                         lua_pushnumber(L, stats->rx_dropped);
454                         lua_setfield(L, -2, "rx_dropped");
455
456                         lua_pushnumber(L, stats->tx_dropped);
457                         lua_setfield(L, -2, "tx_dropped");
458
459                         lua_pushnumber(L, stats->multicast);
460                         lua_setfield(L, -2, "multicast");
461
462                         lua_pushnumber(L, stats->collisions);
463                         lua_setfield(L, -2, "collisions");
464                 } else {
465                         lua_newtable(L);
466                 }
467                 lua_setfield(L, -2, "data");
468 #endif
469
470                 if (c->ifa_netmask && !nixio__addr_parse(&addr, c->ifa_netmask)) {
471                         lua_pushstring(L, addr.host);
472                         lua_setfield(L, -2, "netmask");
473
474                         lua_pushinteger(L, nixio__addr_prefix(c->ifa_netmask));
475                         lua_setfield(L, -2, "prefix");
476                 }
477
478                 if (c->ifa_broadaddr && !nixio__addr_parse(&addr, c->ifa_broadaddr)) {
479                         lua_pushstring(L, addr.host);
480                         lua_setfield(L, -2, "broadaddr");
481                 }
482
483                 if (c->ifa_dstaddr && !nixio__addr_parse(&addr, c->ifa_dstaddr)) {
484                         lua_pushstring(L, addr.host);
485                         lua_setfield(L, -2, "dstaddr");
486                 }
487
488                 lua_rawseti(L, -2, i++);
489         }
490
491         freeifaddrs(ifaddr);
492         return 1;
493 }
494 #endif
495
496
497 /* module table */
498 static const luaL_reg R[] = {
499 #if defined(__linux__) || defined(BSD)
500         {"getifaddrs",  nixio_getifaddrs},
501 #endif
502         {"getaddrinfo", nixio_getaddrinfo},
503         {"getnameinfo", nixio_getnameinfo},
504         {NULL,                  NULL}
505 };
506
507 /* object table */
508 static const luaL_reg M[] = {
509         {"getsockname", nixio_sock_getsockname},
510         {"getpeername", nixio_sock_getpeername},
511         {NULL,                  NULL}
512 };
513
514 void nixio_open_address(lua_State *L) {
515         luaL_register(L, NULL, R);
516
517         lua_pushvalue(L, -2);
518         luaL_register(L, NULL, M);
519         lua_pop(L, 1);
520 }