chiark / gitweb /
util: make malloc0 ask calloc for one block of size n
[elogind.git] / src / shared / socket-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <arpa/inet.h>
26 #include <stdio.h>
27 #include <net/if.h>
28 #include <sys/types.h>
29 #include <stddef.h>
30 #include <netdb.h>
31
32 #include "macro.h"
33 #include "path-util.h"
34 #include "util.h"
35 #include "socket-util.h"
36 #include "missing.h"
37 #include "fileio.h"
38 #include "formats-util.h"
39
40 int socket_address_parse(SocketAddress *a, const char *s) {
41         char *e, *n;
42         unsigned u;
43         int r;
44
45         assert(a);
46         assert(s);
47
48         zero(*a);
49         a->type = SOCK_STREAM;
50
51         if (*s == '[') {
52                 /* IPv6 in [x:.....:z]:p notation */
53
54                 e = strchr(s+1, ']');
55                 if (!e)
56                         return -EINVAL;
57
58                 n = strndupa(s+1, e-s-1);
59
60                 errno = 0;
61                 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
62                         return errno > 0 ? -errno : -EINVAL;
63
64                 e++;
65                 if (*e != ':')
66                         return -EINVAL;
67
68                 e++;
69                 r = safe_atou(e, &u);
70                 if (r < 0)
71                         return r;
72
73                 if (u <= 0 || u > 0xFFFF)
74                         return -EINVAL;
75
76                 a->sockaddr.in6.sin6_family = AF_INET6;
77                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
78                 a->size = sizeof(struct sockaddr_in6);
79
80         } else if (*s == '/') {
81                 /* AF_UNIX socket */
82
83                 size_t l;
84
85                 l = strlen(s);
86                 if (l >= sizeof(a->sockaddr.un.sun_path))
87                         return -EINVAL;
88
89                 a->sockaddr.un.sun_family = AF_UNIX;
90                 memcpy(a->sockaddr.un.sun_path, s, l);
91                 a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
92
93         } else if (*s == '@') {
94                 /* Abstract AF_UNIX socket */
95                 size_t l;
96
97                 l = strlen(s+1);
98                 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
99                         return -EINVAL;
100
101                 a->sockaddr.un.sun_family = AF_UNIX;
102                 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
103                 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
104
105         } else {
106                 e = strchr(s, ':');
107                 if (e) {
108                         r = safe_atou(e+1, &u);
109                         if (r < 0)
110                                 return r;
111
112                         if (u <= 0 || u > 0xFFFF)
113                                 return -EINVAL;
114
115                         n = strndupa(s, e-s);
116
117                         /* IPv4 in w.x.y.z:p notation? */
118                         r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
119                         if (r < 0)
120                                 return -errno;
121
122                         if (r > 0) {
123                                 /* Gotcha, it's a traditional IPv4 address */
124                                 a->sockaddr.in.sin_family = AF_INET;
125                                 a->sockaddr.in.sin_port = htons((uint16_t) u);
126                                 a->size = sizeof(struct sockaddr_in);
127                         } else {
128                                 unsigned idx;
129
130                                 if (strlen(n) > IF_NAMESIZE-1)
131                                         return -EINVAL;
132
133                                 /* Uh, our last resort, an interface name */
134                                 idx = if_nametoindex(n);
135                                 if (idx == 0)
136                                         return -EINVAL;
137
138                                 a->sockaddr.in6.sin6_family = AF_INET6;
139                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
140                                 a->sockaddr.in6.sin6_scope_id = idx;
141                                 a->sockaddr.in6.sin6_addr = in6addr_any;
142                                 a->size = sizeof(struct sockaddr_in6);
143                         }
144                 } else {
145
146                         /* Just a port */
147                         r = safe_atou(s, &u);
148                         if (r < 0)
149                                 return r;
150
151                         if (u <= 0 || u > 0xFFFF)
152                                 return -EINVAL;
153
154                         if (socket_ipv6_is_supported()) {
155                                 a->sockaddr.in6.sin6_family = AF_INET6;
156                                 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
157                                 a->sockaddr.in6.sin6_addr = in6addr_any;
158                                 a->size = sizeof(struct sockaddr_in6);
159                         } else {
160                                 a->sockaddr.in.sin_family = AF_INET;
161                                 a->sockaddr.in.sin_port = htons((uint16_t) u);
162                                 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
163                                 a->size = sizeof(struct sockaddr_in);
164                         }
165                 }
166         }
167
168         return 0;
169 }
170
171 int socket_address_parse_and_warn(SocketAddress *a, const char *s) {
172         SocketAddress b;
173         int r;
174
175         /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */
176
177         r = socket_address_parse(&b, s);
178         if (r < 0)
179                 return r;
180
181         if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) {
182                 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
183                 return -EAFNOSUPPORT;
184         }
185
186         *a = b;
187         return 0;
188 }
189
190 int socket_address_parse_netlink(SocketAddress *a, const char *s) {
191         int family;
192         unsigned group = 0;
193         _cleanup_free_ char *sfamily = NULL;
194         assert(a);
195         assert(s);
196
197         zero(*a);
198         a->type = SOCK_RAW;
199
200         errno = 0;
201         if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
202                 return errno > 0 ? -errno : -EINVAL;
203
204         family = netlink_family_from_string(sfamily);
205         if (family < 0)
206                 return -EINVAL;
207
208         a->sockaddr.nl.nl_family = AF_NETLINK;
209         a->sockaddr.nl.nl_groups = group;
210
211         a->type = SOCK_RAW;
212         a->size = sizeof(struct sockaddr_nl);
213         a->protocol = family;
214
215         return 0;
216 }
217
218 int socket_address_verify(const SocketAddress *a) {
219         assert(a);
220
221         switch (socket_address_family(a)) {
222
223         case AF_INET:
224                 if (a->size != sizeof(struct sockaddr_in))
225                         return -EINVAL;
226
227                 if (a->sockaddr.in.sin_port == 0)
228                         return -EINVAL;
229
230                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
231                         return -EINVAL;
232
233                 return 0;
234
235         case AF_INET6:
236                 if (a->size != sizeof(struct sockaddr_in6))
237                         return -EINVAL;
238
239                 if (a->sockaddr.in6.sin6_port == 0)
240                         return -EINVAL;
241
242                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
243                         return -EINVAL;
244
245                 return 0;
246
247         case AF_UNIX:
248                 if (a->size < offsetof(struct sockaddr_un, sun_path))
249                         return -EINVAL;
250
251                 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
252
253                         if (a->sockaddr.un.sun_path[0] != 0) {
254                                 char *e;
255
256                                 /* path */
257                                 e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
258                                 if (!e)
259                                         return -EINVAL;
260
261                                 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
262                                         return -EINVAL;
263                         }
264                 }
265
266                 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
267                         return -EINVAL;
268
269                 return 0;
270
271         case AF_NETLINK:
272
273                 if (a->size != sizeof(struct sockaddr_nl))
274                         return -EINVAL;
275
276                 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
277                         return -EINVAL;
278
279                 return 0;
280
281         default:
282                 return -EAFNOSUPPORT;
283         }
284 }
285
286 int socket_address_print(const SocketAddress *a, char **ret) {
287         int r;
288
289         assert(a);
290         assert(ret);
291
292         r = socket_address_verify(a);
293         if (r < 0)
294                 return r;
295
296         if (socket_address_family(a) == AF_NETLINK) {
297                 _cleanup_free_ char *sfamily = NULL;
298
299                 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
300                 if (r < 0)
301                         return r;
302
303                 r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
304                 if (r < 0)
305                         return -ENOMEM;
306
307                 return 0;
308         }
309
310         return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret);
311 }
312
313 bool socket_address_can_accept(const SocketAddress *a) {
314         assert(a);
315
316         return
317                 a->type == SOCK_STREAM ||
318                 a->type == SOCK_SEQPACKET;
319 }
320
321 bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
322         assert(a);
323         assert(b);
324
325         /* Invalid addresses are unequal to all */
326         if (socket_address_verify(a) < 0 ||
327             socket_address_verify(b) < 0)
328                 return false;
329
330         if (a->type != b->type)
331                 return false;
332
333         if (socket_address_family(a) != socket_address_family(b))
334                 return false;
335
336         switch (socket_address_family(a)) {
337
338         case AF_INET:
339                 if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
340                         return false;
341
342                 if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
343                         return false;
344
345                 break;
346
347         case AF_INET6:
348                 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
349                         return false;
350
351                 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
352                         return false;
353
354                 break;
355
356         case AF_UNIX:
357                 if (a->size <= offsetof(struct sockaddr_un, sun_path) ||
358                     b->size <= offsetof(struct sockaddr_un, sun_path))
359                         return false;
360
361                 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
362                         return false;
363
364                 if (a->sockaddr.un.sun_path[0]) {
365                         if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path))
366                                 return false;
367                 } else {
368                         if (a->size != b->size)
369                                 return false;
370
371                         if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
372                                 return false;
373                 }
374
375                 break;
376
377         case AF_NETLINK:
378                 if (a->protocol != b->protocol)
379                         return false;
380
381                 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
382                         return false;
383
384                 break;
385
386         default:
387                 /* Cannot compare, so we assume the addresses are different */
388                 return false;
389         }
390
391         return true;
392 }
393
394 bool socket_address_is(const SocketAddress *a, const char *s, int type) {
395         struct SocketAddress b;
396
397         assert(a);
398         assert(s);
399
400         if (socket_address_parse(&b, s) < 0)
401                 return false;
402
403         b.type = type;
404
405         return socket_address_equal(a, &b);
406 }
407
408 bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
409         struct SocketAddress b;
410
411         assert(a);
412         assert(s);
413
414         if (socket_address_parse_netlink(&b, s) < 0)
415                 return false;
416
417         return socket_address_equal(a, &b);
418 }
419
420 const char* socket_address_get_path(const SocketAddress *a) {
421         assert(a);
422
423         if (socket_address_family(a) != AF_UNIX)
424                 return NULL;
425
426         if (a->sockaddr.un.sun_path[0] == 0)
427                 return NULL;
428
429         return a->sockaddr.un.sun_path;
430 }
431
432 bool socket_ipv6_is_supported(void) {
433         _cleanup_free_ char *l = NULL;
434
435         if (access("/sys/module/ipv6", F_OK) != 0)
436                 return false;
437
438         /* If we can't check "disable" parameter, assume enabled */
439         if (read_one_line_file("/sys/module/ipv6/parameters/disable", &l) < 0)
440                 return true;
441
442         /* If module was loaded with disable=1 no IPv6 available */
443         return l[0] == '0';
444 }
445
446 bool socket_address_matches_fd(const SocketAddress *a, int fd) {
447         SocketAddress b;
448         socklen_t solen;
449
450         assert(a);
451         assert(fd >= 0);
452
453         b.size = sizeof(b.sockaddr);
454         if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0)
455                 return false;
456
457         if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family)
458                 return false;
459
460         solen = sizeof(b.type);
461         if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0)
462                 return false;
463
464         if (b.type != a->type)
465                 return false;
466
467         if (a->protocol != 0)  {
468                 solen = sizeof(b.protocol);
469                 if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0)
470                         return false;
471
472                 if (b.protocol != a->protocol)
473                         return false;
474         }
475
476         return socket_address_equal(a, &b);
477 }
478
479 int sockaddr_port(const struct sockaddr *_sa) {
480         union sockaddr_union *sa = (union sockaddr_union*) _sa;
481
482         assert(sa);
483
484         if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
485                 return -EAFNOSUPPORT;
486
487         return ntohs(sa->sa.sa_family == AF_INET6 ?
488                        sa->in6.sin6_port :
489                        sa->in.sin_port);
490 }
491
492 int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
493         union sockaddr_union *sa = (union sockaddr_union*) _sa;
494         char *p;
495         int r;
496
497         assert(sa);
498         assert(salen >= sizeof(sa->sa.sa_family));
499
500         switch (sa->sa.sa_family) {
501
502         case AF_INET: {
503                 uint32_t a;
504
505                 a = ntohl(sa->in.sin_addr.s_addr);
506
507                 if (include_port)
508                         r = asprintf(&p,
509                                      "%u.%u.%u.%u:%u",
510                                      a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
511                                      ntohs(sa->in.sin_port));
512                 else
513                         r = asprintf(&p,
514                                      "%u.%u.%u.%u",
515                                      a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF);
516                 if (r < 0)
517                         return -ENOMEM;
518                 break;
519         }
520
521         case AF_INET6: {
522                 static const unsigned char ipv4_prefix[] = {
523                         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
524                 };
525
526                 if (translate_ipv6 &&
527                     memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
528                         const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
529                         if (include_port)
530                                 r = asprintf(&p,
531                                              "%u.%u.%u.%u:%u",
532                                              a[0], a[1], a[2], a[3],
533                                              ntohs(sa->in6.sin6_port));
534                         else
535                                 r = asprintf(&p,
536                                              "%u.%u.%u.%u",
537                                              a[0], a[1], a[2], a[3]);
538                         if (r < 0)
539                                 return -ENOMEM;
540                 } else {
541                         char a[INET6_ADDRSTRLEN];
542
543                         inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a));
544
545                         if (include_port) {
546                                 r = asprintf(&p,
547                                              "[%s]:%u",
548                                              a,
549                                              ntohs(sa->in6.sin6_port));
550                                 if (r < 0)
551                                         return -ENOMEM;
552                         } else {
553                                 p = strdup(a);
554                                 if (!p)
555                                         return -ENOMEM;
556                         }
557                 }
558
559                 break;
560         }
561
562         case AF_UNIX:
563                 if (salen <= offsetof(struct sockaddr_un, sun_path)) {
564                         p = strdup("<unnamed>");
565                         if (!p)
566                                 return -ENOMEM;
567
568                 } else if (sa->un.sun_path[0] == 0) {
569                         /* abstract */
570
571                         /* FIXME: We assume we can print the
572                          * socket path here and that it hasn't
573                          * more than one NUL byte. That is
574                          * actually an invalid assumption */
575
576                         p = new(char, sizeof(sa->un.sun_path)+1);
577                         if (!p)
578                                 return -ENOMEM;
579
580                         p[0] = '@';
581                         memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
582                         p[sizeof(sa->un.sun_path)] = 0;
583
584                 } else {
585                         p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
586                         if (!ret)
587                                 return -ENOMEM;
588                 }
589
590                 break;
591
592         default:
593                 return -EOPNOTSUPP;
594         }
595
596
597         *ret = p;
598         return 0;
599 }
600
601 int getpeername_pretty(int fd, char **ret) {
602         union sockaddr_union sa;
603         socklen_t salen = sizeof(sa);
604         int r;
605
606         assert(fd >= 0);
607         assert(ret);
608
609         if (getpeername(fd, &sa.sa, &salen) < 0)
610                 return -errno;
611
612         if (sa.sa.sa_family == AF_UNIX) {
613                 struct ucred ucred = {};
614
615                 /* UNIX connection sockets are anonymous, so let's use
616                  * PID/UID as pretty credentials instead */
617
618                 r = getpeercred(fd, &ucred);
619                 if (r < 0)
620                         return r;
621
622                 if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0)
623                         return -ENOMEM;
624
625                 return 0;
626         }
627
628         /* For remote sockets we translate IPv6 addresses back to IPv4
629          * if applicable, since that's nicer. */
630
631         return sockaddr_pretty(&sa.sa, salen, true, true, ret);
632 }
633
634 int getsockname_pretty(int fd, char **ret) {
635         union sockaddr_union sa;
636         socklen_t salen = sizeof(sa);
637
638         assert(fd >= 0);
639         assert(ret);
640
641         if (getsockname(fd, &sa.sa, &salen) < 0)
642                 return -errno;
643
644         /* For local sockets we do not translate IPv6 addresses back
645          * to IPv6 if applicable, since this is usually used for
646          * listening sockets where the difference between IPv4 and
647          * IPv6 matters. */
648
649         return sockaddr_pretty(&sa.sa, salen, false, true, ret);
650 }
651
652 int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) {
653         int r;
654         char host[NI_MAXHOST], *ret;
655
656         assert(_ret);
657
658         r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0,
659                         NI_IDN|NI_IDN_USE_STD3_ASCII_RULES);
660         if (r != 0) {
661                 int saved_errno = errno;
662
663                 r = sockaddr_pretty(&sa->sa, salen, true, true, &ret);
664                 if (r < 0)
665                         return r;
666
667                 log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret);
668         } else {
669                 ret = strdup(host);
670                 if (!ret)
671                         return -ENOMEM;
672         }
673
674         *_ret = ret;
675         return 0;
676 }
677
678 int getnameinfo_pretty(int fd, char **ret) {
679         union sockaddr_union sa;
680         socklen_t salen = sizeof(sa);
681
682         assert(fd >= 0);
683         assert(ret);
684
685         if (getsockname(fd, &sa.sa, &salen) < 0)
686                 return -errno;
687
688         return socknameinfo_pretty(&sa, salen, ret);
689 }
690
691 int socket_address_unlink(SocketAddress *a) {
692         assert(a);
693
694         if (socket_address_family(a) != AF_UNIX)
695                 return 0;
696
697         if (a->sockaddr.un.sun_path[0] == 0)
698                 return 0;
699
700         if (unlink(a->sockaddr.un.sun_path) < 0)
701                 return -errno;
702
703         return 1;
704 }
705
706 static const char* const netlink_family_table[] = {
707         [NETLINK_ROUTE] = "route",
708         [NETLINK_FIREWALL] = "firewall",
709         [NETLINK_INET_DIAG] = "inet-diag",
710         [NETLINK_NFLOG] = "nflog",
711         [NETLINK_XFRM] = "xfrm",
712         [NETLINK_SELINUX] = "selinux",
713         [NETLINK_ISCSI] = "iscsi",
714         [NETLINK_AUDIT] = "audit",
715         [NETLINK_FIB_LOOKUP] = "fib-lookup",
716         [NETLINK_CONNECTOR] = "connector",
717         [NETLINK_NETFILTER] = "netfilter",
718         [NETLINK_IP6_FW] = "ip6-fw",
719         [NETLINK_DNRTMSG] = "dnrtmsg",
720         [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
721         [NETLINK_GENERIC] = "generic",
722         [NETLINK_SCSITRANSPORT] = "scsitransport",
723         [NETLINK_ECRYPTFS] = "ecryptfs"
724 };
725
726 DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
727
728 static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
729         [SOCKET_ADDRESS_DEFAULT] = "default",
730         [SOCKET_ADDRESS_BOTH] = "both",
731         [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
732 };
733
734 DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
735
736 bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
737         assert(a);
738         assert(b);
739
740         if (a->sa.sa_family != b->sa.sa_family)
741                 return false;
742
743         if (a->sa.sa_family == AF_INET)
744                 return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr;
745
746         if (a->sa.sa_family == AF_INET6)
747                 return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
748
749         return false;
750 }
751
752 char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) {
753         assert(addr);
754         assert(buffer);
755
756         /* Like ether_ntoa() but uses %02x instead of %x to print
757          * ethernet addresses, which makes them look less funny. Also,
758          * doesn't use a static buffer. */
759
760         sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x",
761                 addr->ether_addr_octet[0],
762                 addr->ether_addr_octet[1],
763                 addr->ether_addr_octet[2],
764                 addr->ether_addr_octet[3],
765                 addr->ether_addr_octet[4],
766                 addr->ether_addr_octet[5]);
767
768         return buffer;
769 }