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