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