]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/socket-util.c
sleep-config: fix useless check for swapfile type
[thirdparty/systemd.git] / src / shared / socket-util.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
42f4e3c4 2
a7334b09
LP
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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
42f4e3c4
LP
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>
542563ba 29#include <net/if.h>
b5a0699f
LP
30#include <sys/types.h>
31#include <sys/stat.h>
0e098b15
LP
32#include <stddef.h>
33#include <sys/ioctl.h>
42f4e3c4
LP
34
35#include "macro.h"
36#include "util.h"
49e942b2 37#include "mkdir.h"
9eb977db 38#include "path-util.h"
42f4e3c4 39#include "socket-util.h"
16c42ce1 40#include "missing.h"
a5c32cff 41#include "fileio.h"
42f4e3c4 42
542563ba 43int socket_address_parse(SocketAddress *a, const char *s) {
42f4e3c4
LP
44 char *e, *n;
45 unsigned u;
4d49b48c 46 int r;
42f4e3c4
LP
47
48 assert(a);
49 assert(s);
50
9152c765 51 zero(*a);
542563ba 52 a->type = SOCK_STREAM;
42f4e3c4
LP
53
54 if (*s == '[') {
55 /* IPv6 in [x:.....:z]:p notation */
56
5bfcc1c6
FF
57 if (!socket_ipv6_is_supported()) {
58 log_warning("Binding to IPv6 address not available since kernel does not support IPv6.");
59 return -EAFNOSUPPORT;
60 }
61
4d49b48c
LP
62 e = strchr(s+1, ']');
63 if (!e)
42f4e3c4
LP
64 return -EINVAL;
65
4d49b48c 66 n = strndupa(s+1, e-s-1);
42f4e3c4
LP
67
68 errno = 0;
4d49b48c 69 if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0)
8333c77e 70 return errno > 0 ? -errno : -EINVAL;
42f4e3c4
LP
71
72 e++;
73 if (*e != ':')
74 return -EINVAL;
75
76 e++;
4d49b48c
LP
77 r = safe_atou(e, &u);
78 if (r < 0)
42f4e3c4
LP
79 return r;
80
81 if (u <= 0 || u > 0xFFFF)
82 return -EINVAL;
83
84 a->sockaddr.in6.sin6_family = AF_INET6;
85 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
86 a->size = sizeof(struct sockaddr_in6);
42f4e3c4
LP
87
88 } else if (*s == '/') {
89 /* AF_UNIX socket */
90
91 size_t l;
92
93 l = strlen(s);
94 if (l >= sizeof(a->sockaddr.un.sun_path))
95 return -EINVAL;
96
97 a->sockaddr.un.sun_family = AF_UNIX;
98 memcpy(a->sockaddr.un.sun_path, s, l);
0e098b15 99 a->size = offsetof(struct sockaddr_un, sun_path) + l + 1;
42f4e3c4 100
1c24e7bd 101 } else if (*s == '@') {
42f4e3c4
LP
102 /* Abstract AF_UNIX socket */
103 size_t l;
104
105 l = strlen(s+1);
106 if (l >= sizeof(a->sockaddr.un.sun_path) - 1)
107 return -EINVAL;
108
109 a->sockaddr.un.sun_family = AF_UNIX;
110 memcpy(a->sockaddr.un.sun_path+1, s+1, l);
0e098b15 111 a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
42f4e3c4
LP
112
113 } else {
4d49b48c
LP
114 e = strchr(s, ':');
115 if (e) {
116 r = safe_atou(e+1, &u);
117 if (r < 0)
542563ba
LP
118 return r;
119
120 if (u <= 0 || u > 0xFFFF)
121 return -EINVAL;
42f4e3c4 122
4d49b48c 123 n = strndupa(s, e-s);
42f4e3c4 124
542563ba 125 /* IPv4 in w.x.y.z:p notation? */
4d49b48c
LP
126 r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr);
127 if (r < 0)
542563ba 128 return -errno;
42f4e3c4 129
542563ba
LP
130 if (r > 0) {
131 /* Gotcha, it's a traditional IPv4 address */
4d49b48c
LP
132 a->sockaddr.in.sin_family = AF_INET;
133 a->sockaddr.in.sin_port = htons((uint16_t) u);
542563ba
LP
134 a->size = sizeof(struct sockaddr_in);
135 } else {
136 unsigned idx;
42f4e3c4 137
4d49b48c 138 if (strlen(n) > IF_NAMESIZE-1)
acbb0225 139 return -EINVAL;
acbb0225 140
542563ba
LP
141 /* Uh, our last resort, an interface name */
142 idx = if_nametoindex(n);
83c60c9f 143 if (idx == 0)
542563ba 144 return -EINVAL;
42f4e3c4 145
5bfcc1c6
FF
146 if (!socket_ipv6_is_supported()) {
147 log_warning("Binding to interface is not available since kernel does not support IPv6.");
148 return -EAFNOSUPPORT;
149 }
150
542563ba
LP
151 a->sockaddr.in6.sin6_family = AF_INET6;
152 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
153 a->sockaddr.in6.sin6_scope_id = idx;
83c60c9f 154 a->sockaddr.in6.sin6_addr = in6addr_any;
542563ba
LP
155 a->size = sizeof(struct sockaddr_in6);
156 }
42f4e3c4
LP
157 } else {
158
159 /* Just a port */
5198dabc
LP
160 r = safe_atou(s, &u);
161 if (r < 0)
42f4e3c4
LP
162 return r;
163
164 if (u <= 0 || u > 0xFFFF)
165 return -EINVAL;
166
5bfcc1c6
FF
167 if (socket_ipv6_is_supported()) {
168 a->sockaddr.in6.sin6_family = AF_INET6;
169 a->sockaddr.in6.sin6_port = htons((uint16_t) u);
170 a->sockaddr.in6.sin6_addr = in6addr_any;
171 a->size = sizeof(struct sockaddr_in6);
172 } else {
4d49b48c
LP
173 a->sockaddr.in.sin_family = AF_INET;
174 a->sockaddr.in.sin_port = htons((uint16_t) u);
175 a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
5bfcc1c6
FF
176 a->size = sizeof(struct sockaddr_in);
177 }
42f4e3c4
LP
178 }
179 }
180
181 return 0;
182}
183
7a22745a
LP
184int socket_address_parse_netlink(SocketAddress *a, const char *s) {
185 int family;
186 unsigned group = 0;
f8b69d1d 187 _cleanup_free_ char *sfamily = NULL;
7a22745a
LP
188 assert(a);
189 assert(s);
190
191 zero(*a);
192 a->type = SOCK_RAW;
193
194 errno = 0;
195 if (sscanf(s, "%ms %u", &sfamily, &group) < 1)
bcb161b0 196 return errno > 0 ? -errno : -EINVAL;
7a22745a 197
f8b69d1d
MS
198 family = netlink_family_from_string(sfamily);
199 if (family < 0)
200 return -EINVAL;
7a22745a
LP
201
202 a->sockaddr.nl.nl_family = AF_NETLINK;
203 a->sockaddr.nl.nl_groups = group;
204
205 a->type = SOCK_RAW;
206 a->size = sizeof(struct sockaddr_nl);
207 a->protocol = family;
208
209 return 0;
210}
211
542563ba 212int socket_address_verify(const SocketAddress *a) {
42f4e3c4
LP
213 assert(a);
214
542563ba 215 switch (socket_address_family(a)) {
42f4e3c4 216
7a22745a
LP
217 case AF_INET:
218 if (a->size != sizeof(struct sockaddr_in))
219 return -EINVAL;
42f4e3c4 220
4d49b48c 221 if (a->sockaddr.in.sin_port == 0)
7a22745a 222 return -EINVAL;
42f4e3c4 223
7a22745a
LP
224 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
225 return -EINVAL;
42f4e3c4 226
7a22745a
LP
227 return 0;
228
229 case AF_INET6:
230 if (a->size != sizeof(struct sockaddr_in6))
231 return -EINVAL;
42f4e3c4 232
7a22745a
LP
233 if (a->sockaddr.in6.sin6_port == 0)
234 return -EINVAL;
42f4e3c4 235
7a22745a
LP
236 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
237 return -EINVAL;
42f4e3c4 238
7a22745a 239 return 0;
42f4e3c4 240
7a22745a
LP
241 case AF_UNIX:
242 if (a->size < offsetof(struct sockaddr_un, sun_path))
243 return -EINVAL;
42f4e3c4 244
7a22745a 245 if (a->size > offsetof(struct sockaddr_un, sun_path)) {
42f4e3c4 246
7a22745a
LP
247 if (a->sockaddr.un.sun_path[0] != 0) {
248 char *e;
249
250 /* path */
4d49b48c
LP
251 e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path));
252 if (!e)
7a22745a
LP
253 return -EINVAL;
254
255 if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1)
256 return -EINVAL;
42f4e3c4 257 }
7a22745a 258 }
42f4e3c4 259
5a2b80ce 260 if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET)
7a22745a 261 return -EINVAL;
42f4e3c4 262
7a22745a
LP
263 return 0;
264
265 case AF_NETLINK:
266
267 if (a->size != sizeof(struct sockaddr_nl))
268 return -EINVAL;
269
270 if (a->type != SOCK_RAW && a->type != SOCK_DGRAM)
271 return -EINVAL;
272
273 return 0;
274
275 default:
276 return -EAFNOSUPPORT;
42f4e3c4
LP
277 }
278}
279
4d49b48c 280int socket_address_print(const SocketAddress *a, char **ret) {
42f4e3c4 281 int r;
4d49b48c 282
42f4e3c4 283 assert(a);
4d49b48c 284 assert(ret);
42f4e3c4 285
4d49b48c
LP
286 r = socket_address_verify(a);
287 if (r < 0)
42f4e3c4
LP
288 return r;
289
4d49b48c 290 if (socket_address_family(a) == AF_NETLINK) {
7fd1b19b 291 _cleanup_free_ char *sfamily = NULL;
7a22745a 292
f8b69d1d 293 r = netlink_family_to_string_alloc(a->protocol, &sfamily);
7a22745a 294 if (r < 0)
f8b69d1d 295 return r;
4d49b48c
LP
296
297 r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups);
8520cfa5
MS
298 if (r < 0)
299 return -ENOMEM;
7a22745a
LP
300
301 return 0;
302 }
303
4d49b48c 304 return sockaddr_pretty(&a->sockaddr.sa, a->size, false, ret);
42f4e3c4
LP
305}
306
4f2d528d
LP
307bool socket_address_can_accept(const SocketAddress *a) {
308 assert(a);
309
310 return
311 a->type == SOCK_STREAM ||
312 a->type == SOCK_SEQPACKET;
313}
a16e1123
LP
314
315bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
316 assert(a);
317 assert(b);
318
319 /* Invalid addresses are unequal to all */
320 if (socket_address_verify(a) < 0 ||
321 socket_address_verify(b) < 0)
322 return false;
323
324 if (a->type != b->type)
325 return false;
326
327 if (a->size != b->size)
328 return false;
329
330 if (socket_address_family(a) != socket_address_family(b))
331 return false;
332
333 switch (socket_address_family(a)) {
334
335 case AF_INET:
4d49b48c 336 if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr)
a16e1123
LP
337 return false;
338
4d49b48c 339 if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port)
a16e1123
LP
340 return false;
341
342 break;
343
344 case AF_INET6:
345 if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0)
346 return false;
347
348 if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port)
349 return false;
350
351 break;
352
353 case AF_UNIX:
354
355 if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0))
356 return false;
357
358 if (a->sockaddr.un.sun_path[0]) {
641906e9 359 if (!strneq(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, sizeof(a->sockaddr.un.sun_path)))
a16e1123
LP
360 return false;
361 } else {
b12c1e7c 362 if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0)
a16e1123
LP
363 return false;
364 }
365
366 break;
367
7a22745a
LP
368 case AF_NETLINK:
369
370 if (a->protocol != b->protocol)
371 return false;
372
373 if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups)
374 return false;
375
376 break;
377
a16e1123
LP
378 default:
379 /* Cannot compare, so we assume the addresses are different */
380 return false;
381 }
382
383 return true;
384}
385
27ca8d7a 386bool socket_address_is(const SocketAddress *a, const char *s, int type) {
a16e1123
LP
387 struct SocketAddress b;
388
389 assert(a);
390 assert(s);
391
392 if (socket_address_parse(&b, s) < 0)
393 return false;
394
27ca8d7a
LP
395 b.type = type;
396
a16e1123 397 return socket_address_equal(a, &b);
6e2ef85b
LP
398}
399
7a22745a
LP
400bool socket_address_is_netlink(const SocketAddress *a, const char *s) {
401 struct SocketAddress b;
402
403 assert(a);
404 assert(s);
405
406 if (socket_address_parse_netlink(&b, s) < 0)
407 return false;
408
409 return socket_address_equal(a, &b);
410}
411
a57f7e2c 412const char* socket_address_get_path(const SocketAddress *a) {
6e2ef85b
LP
413 assert(a);
414
415 if (socket_address_family(a) != AF_UNIX)
a57f7e2c 416 return NULL;
6e2ef85b
LP
417
418 if (a->sockaddr.un.sun_path[0] == 0)
a57f7e2c 419 return NULL;
a16e1123 420
a57f7e2c 421 return a->sockaddr.un.sun_path;
a16e1123 422}
c0120d99 423
5bfcc1c6 424bool socket_ipv6_is_supported(void) {
f89f1e8f
AB
425 char *l = 0;
426 bool enabled;
427
428 if (access("/sys/module/ipv6", F_OK) != 0)
429 return 0;
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 1;
434
435 /* If module was loaded with disable=1 no IPv6 available */
436 enabled = l[0] == '0';
437 free(l);
438
439 return enabled;
5bfcc1c6
FF
440}
441
01e10de3
LP
442bool socket_address_matches_fd(const SocketAddress *a, int fd) {
443 union sockaddr_union sa;
444 socklen_t salen = sizeof(sa), solen;
445 int protocol, type;
446
447 assert(a);
448 assert(fd >= 0);
449
450 if (getsockname(fd, &sa.sa, &salen) < 0)
451 return false;
452
453 if (sa.sa.sa_family != a->sockaddr.sa.sa_family)
454 return false;
455
456 solen = sizeof(type);
457 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &solen) < 0)
458 return false;
459
460 if (type != a->type)
461 return false;
462
463 if (a->protocol != 0) {
464 solen = sizeof(protocol);
465 if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &solen) < 0)
466 return false;
467
468 if (protocol != a->protocol)
469 return false;
470 }
471
472 switch (sa.sa.sa_family) {
473
474 case AF_INET:
4d49b48c
LP
475 return sa.in.sin_port == a->sockaddr.in.sin_port &&
476 sa.in.sin_addr.s_addr == a->sockaddr.in.sin_addr.s_addr;
01e10de3
LP
477
478 case AF_INET6:
479 return sa.in6.sin6_port == a->sockaddr.in6.sin6_port &&
480 memcmp(&sa.in6.sin6_addr, &a->sockaddr.in6.sin6_addr, sizeof(struct in6_addr)) == 0;
481
482 case AF_UNIX:
483 return salen == a->size &&
484 memcmp(sa.un.sun_path, a->sockaddr.un.sun_path, salen - offsetof(struct sockaddr_un, sun_path)) == 0;
485
486 }
487
488 return false;
489}
490
4d49b48c
LP
491int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, char **ret) {
492 union sockaddr_union *sa = (union sockaddr_union*) _sa;
8569a776
LP
493 char *p;
494
4d49b48c
LP
495 assert(sa);
496 assert(salen >= sizeof(sa->sa.sa_family));
8569a776 497
4d49b48c 498 switch (sa->sa.sa_family) {
8569a776
LP
499
500 case AF_INET: {
501 uint32_t a;
502
4d49b48c 503 a = ntohl(sa->in.sin_addr.s_addr);
8569a776
LP
504
505 if (asprintf(&p,
506 "%u.%u.%u.%u:%u",
507 a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF,
4d49b48c 508 ntohs(sa->in.sin_port)) < 0)
8569a776
LP
509 return -ENOMEM;
510
511 break;
512 }
513
514 case AF_INET6: {
515 static const unsigned char ipv4_prefix[] = {
516 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF
517 };
518
4d49b48c
LP
519 if (translate_ipv6 && memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) {
520 const uint8_t *a = sa->in6.sin6_addr.s6_addr+12;
8569a776
LP
521
522 if (asprintf(&p,
523 "%u.%u.%u.%u:%u",
524 a[0], a[1], a[2], a[3],
4d49b48c 525 ntohs(sa->in6.sin6_port)) < 0)
8569a776
LP
526 return -ENOMEM;
527 } else {
528 char a[INET6_ADDRSTRLEN];
529
530 if (asprintf(&p,
4d49b48c
LP
531 "[%s]:%u",
532 inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)),
533 ntohs(sa->in6.sin6_port)) < 0)
8569a776
LP
534 return -ENOMEM;
535 }
536
537 break;
538 }
539
4d49b48c
LP
540 case AF_UNIX:
541 if (salen <= offsetof(struct sockaddr_un, sun_path)) {
542 p = strdup("<unnamed>");
543 if (!p)
544 return -ENOMEM;
8569a776 545
4d49b48c
LP
546 } else if (sa->un.sun_path[0] == 0) {
547 /* abstract */
8569a776 548
4d49b48c
LP
549 /* FIXME: We assume we can print the
550 * socket path here and that it hasn't
551 * more than one NUL byte. That is
552 * actually an invalid assumption */
553
554 p = new(char, sizeof(sa->un.sun_path)+1);
555 if (!p)
556 return -ENOMEM;
557
558 p[0] = '@';
559 memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1);
560 p[sizeof(sa->un.sun_path)] = 0;
561
562 } else {
563 p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path));
564 if (!ret)
565 return -ENOMEM;
566 }
8569a776
LP
567
568 break;
8569a776
LP
569
570 default:
571 return -ENOTSUP;
572 }
573
4d49b48c 574
8569a776
LP
575 *ret = p;
576 return 0;
577}
578
4d49b48c
LP
579int getpeername_pretty(int fd, char **ret) {
580 union sockaddr_union sa;
581 socklen_t salen;
eff05270 582 int r;
4d49b48c
LP
583
584 assert(fd >= 0);
585 assert(ret);
586
587 salen = sizeof(sa);
588 if (getpeername(fd, &sa.sa, &salen) < 0)
589 return -errno;
590
591 if (sa.sa.sa_family == AF_UNIX) {
592 struct ucred ucred;
593
594 /* UNIX connection sockets are anonymous, so let's use
595 * PID/UID as pretty credentials instead */
596
eff05270
LP
597 r = getpeercred(fd, &ucred);
598 if (r < 0)
599 return r;
4d49b48c
LP
600
601 if (asprintf(ret, "PID %lu/UID %lu", (unsigned long) ucred.pid, (unsigned long) ucred.pid) < 0)
602 return -ENOMEM;
603
604 return 0;
605 }
606
607 /* For remote sockets we translate IPv6 addresses back to IPv4
608 * if applicable, since that's nicer. */
609
610 return sockaddr_pretty(&sa.sa, salen, true, ret);
611}
612
613int getsockname_pretty(int fd, char **ret) {
614 union sockaddr_union sa;
615 socklen_t salen;
616
617 assert(fd >= 0);
618 assert(ret);
619
620 salen = sizeof(sa);
621 if (getsockname(fd, &sa.sa, &salen) < 0)
622 return -errno;
623
624 /* For local sockets we do not translate IPv6 addresses back
625 * to IPv6 if applicable, since this is usually used for
626 * listening sockets where the difference between IPv4 and
627 * IPv6 matters. */
628
629 return sockaddr_pretty(&sa.sa, salen, false, ret);
630}
631
7a22745a
LP
632static const char* const netlink_family_table[] = {
633 [NETLINK_ROUTE] = "route",
634 [NETLINK_FIREWALL] = "firewall",
635 [NETLINK_INET_DIAG] = "inet-diag",
636 [NETLINK_NFLOG] = "nflog",
637 [NETLINK_XFRM] = "xfrm",
638 [NETLINK_SELINUX] = "selinux",
639 [NETLINK_ISCSI] = "iscsi",
640 [NETLINK_AUDIT] = "audit",
641 [NETLINK_FIB_LOOKUP] = "fib-lookup",
642 [NETLINK_CONNECTOR] = "connector",
643 [NETLINK_NETFILTER] = "netfilter",
644 [NETLINK_IP6_FW] = "ip6-fw",
645 [NETLINK_DNRTMSG] = "dnrtmsg",
646 [NETLINK_KOBJECT_UEVENT] = "kobject-uevent",
647 [NETLINK_GENERIC] = "generic",
648 [NETLINK_SCSITRANSPORT] = "scsitransport",
649 [NETLINK_ECRYPTFS] = "ecryptfs"
650};
651
f8b69d1d 652DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX);
7a22745a 653
c0120d99
LP
654static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = {
655 [SOCKET_ADDRESS_DEFAULT] = "default",
656 [SOCKET_ADDRESS_BOTH] = "both",
657 [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only"
658};
659
660DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);