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