]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
IO: Changes in socket API
authorVojtech Vilimek <vojtech.vilimek@nic.cz>
Wed, 17 Jul 2024 10:05:13 +0000 (12:05 +0200)
committerVojtech Vilimek <vojtech.vilimek@nic.cz>
Thu, 18 Jul 2024 17:53:54 +0000 (19:53 +0200)
Support for active UNIX sockets is added. UNIX socket are now created with
sk_open. The socket name/path is passes in host, the same way as SSH address.

lib/socket.h
sysdep/unix/io.c
sysdep/unix/main.c
sysdep/unix/unix.h

index 5c69482ed9ba84262e7b540eeef776d82274f473..dd9004c21d8fd9e46e2415301e3c2f2d0d7d22ed 100644 (file)
@@ -44,7 +44,7 @@ typedef struct birdsock {
   int subtype;                         /* Socket subtype */
   void *data;                          /* User data */
   ip_addr saddr, daddr;                        /* IPA_NONE = unspecified */
-  const char *host;                    /* Alternative to daddr, NULL = unspecified */
+  const char *host;                    /* Alternative to daddr especially for UNIX sockets, NULL = unspecified */
   uint sport, dport;                   /* 0 = unspecified (for IP: protocol type) */
   int tos;                             /* TOS / traffic class, -1 = default */
   int priority;                                /* Local socket priority, -1 = default */
@@ -98,6 +98,7 @@ void sk_dump_all(void);
 
 int sk_is_ipv4(sock *s);               /* True if socket is IPv4 */
 int sk_is_ipv6(sock *s);               /* True if socket is IPv6 */
+int sk_is_unix(sock *s);               /* True if socket is UNIX socket */
 
 static inline int sk_tx_buffer_empty(sock *sk)
 { return sk->tbuf == sk->tpos; }
@@ -111,6 +112,7 @@ int sk_set_min_ttl(sock *s, int ttl);       /* Set minimal accepted TTL for given sock
 int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey);
 int sk_set_ipv6_checksum(sock *s, int offset);
 int sk_set_icmp6_filter(sock *s, int p1, int p2);
+int sk_check_unix(const char *path);
 void sk_log_error(sock *s, const char *p);
 
 byte * sk_rx_buffer(sock *s, int *len);        /* Temporary */
@@ -143,10 +145,11 @@ extern int sk_priority_control;           /* Suggested priority for control traffic, shou
 #define SK_UDP         3          /* ?  ?  ?  ?  ?  ?   ?      */
 #define SK_IP          5          /* ?  -  ?  *  ?  ?   ?      */
 #define SK_MAGIC       7          /* Internal use by sysdep code */
-#define SK_UNIX_PASSIVE        8
-#define SK_UNIX                9
-#define SK_SSH_ACTIVE  10         /* -  -  *  *  -  ?   -      DA = host */
-#define SK_SSH         11
+#define SK_UNIX_PASSIVE        8          /* -  -  *  -  -  -   -      DA = host */
+#define SK_UNIX_ACTIVE 9          /* -  -  *  -  -  -   -      DA = host */
+#define SK_UNIX                10
+#define SK_SSH_ACTIVE  11         /* -  -  *  *  -  ?   -      DA = host */
+#define SK_SSH         12
 
 /*
  *     Socket subtypes
@@ -177,6 +180,10 @@ extern int sk_priority_control;            /* Suggested priority for control traffic, shou
  * per-packet basis using platform dependent options (but these are not
  * available in some corner cases). The first way is used when SKF_BIND is
  * specified, the second way is used otherwise.
+ *
+ * For UNIX sockets (SK_UNIX_PASSIVE, SK_UNIX_ACTIVE), path is passed in host
+ * string. If the path does not fit into the sockaddr_un sun_path array, the
+ * sk_open will fail. One can check path length with sk_check_unix.
  */
 
 #endif
index 23baffb2851d4f35f20cfe2a80bd9b36558a3301..e847ef11175be32fc6692ab4299b106ccea9b673 100644 (file)
@@ -536,6 +536,21 @@ sk_setup_multicast(sock *s)
     return sk_setup_multicast6(s);
 }
 
+/**
+ * sk_check_unix - check path length
+ * @path: filesystem path
+ *
+ * Check if path fits into the sockaddr_un sun_path array.
+ *
+ * Result: 0 for success, -1 for an error.
+ */
+
+int
+sk_check_unix(const char *path)
+{
+  return -(strlen(path) >= sizeof(((struct sockaddr_un *)0)->sun_path));
+}
+
 /**
  * sk_join_group - join multicast group for given socket
  * @s: socket
@@ -866,7 +881,7 @@ static void
 sk_dump(resource *r)
 {
   sock *s = (sock *) r;
-  static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX", "SSH>", "SSH", "DEL!" };
+  static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX>", "UNIX", "SSH>", "SSH", "DEL!" };
 
   debug("(%s, ud=%p, sa=%I, sp=%d, da=%I, dp=%d, tos=%d, ttl=%d, if=%s)\n",
        sk_type_names[s->type],
@@ -923,6 +938,12 @@ sk_setup(sock *s)
   if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
     ERR("O_NONBLOCK");
 
+  if (s->type == SK_UNIX_ACTIVE || s->type == SK_UNIX_PASSIVE)
+    return sk_check_unix(s->host);
+
+  if (s->type == SK_UNIX)
+    return 0;
+
   if (!s->af)
     return 0;
 
@@ -1045,6 +1066,14 @@ sk_tcp_connected(sock *s)
   s->tx_hook(s);
 }
 
+static void
+sk_unix_connected(sock *s)
+{
+  s->type = SK_UNIX;
+  sk_alloc_bufs(s);
+  s->tx_hook(s);
+}
+
 #ifdef HAVE_LIBSSH
 static void
 sk_ssh_connected(sock *s)
@@ -1070,6 +1099,7 @@ sk_passive_connected(sock *s, int type)
     return 0;
   }
 
+  /* Do not copy UNIX address in s->host. */
   sock *t = sk_new(s->pool);
   t->type = type;
   t->data = s->data;
@@ -1327,6 +1357,9 @@ sk_open(sock *s)
   int bind_port = 0;
   ip_addr bind_addr = IPA_NONE;
   sockaddr sa;
+  struct sockaddr_un un;
+  struct sockaddr *addr;
+  socklen_t len;
 
   if (s->type <= SK_IP)
   {
@@ -1399,6 +1432,15 @@ sk_open(sock *s)
     fd = s->fd;
     break;
 
+  case SK_UNIX_ACTIVE:
+    s->ttx = "";
+    /* Fall thru */
+  case SK_UNIX_PASSIVE:
+    af = AF_UNIX;
+    fd = socket(af, SOCK_STREAM, 0);
+    do_bind = s->type == SK_UNIX_PASSIVE;
+    break;
+
   default:
     bug("sk_open() called for invalid sock type %d", s->type);
   }
@@ -1439,8 +1481,21 @@ sk_open(sock *s)
       if (sk_set_freebind(s) < 0)
         log(L_WARN "Socket error: %s%#m", s->err);
 
-    sockaddr_fill(&sa, s->af, bind_addr, s->iface, bind_port);
-    if (bind(fd, &sa.sa, SA_LEN(sa)) < 0)
+    if (s->type == SK_UNIX_PASSIVE)
+    {
+      un.sun_family = AF_UNIX;
+      strcpy(un.sun_path, s->host);
+      addr = (struct sockaddr *) &un;
+      len = SUN_LEN(&un);
+    }
+    else
+    {
+      sockaddr_fill(&sa, s->af, bind_addr, s->iface, bind_port);
+      addr = &sa.sa;
+      len = SA_LEN(sa);
+    }
+
+    if (bind(fd, addr, len) < 0)
       ERR2("bind");
   }
 
@@ -1464,6 +1519,21 @@ sk_open(sock *s)
       ERR2("listen");
     break;
 
+  case SK_UNIX_ACTIVE:
+    un.sun_family = AF_UNIX;
+    strcpy(un.sun_path, s->host);
+
+    if (connect(s->fd, (struct sockaddr *) &un, SUN_LEN(&un)) >= 0)
+      sk_unix_connected(s);
+    else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS)
+      ERR2("connect");
+    break;
+
+  case SK_UNIX_PASSIVE:
+    if (listen(fd, 8) < 0)
+      ERR2("listen");
+    break;
+
   case SK_SSH_ACTIVE:
   case SK_MAGIC:
     break;
@@ -1483,38 +1553,6 @@ err:
   return -1;
 }
 
-int
-sk_open_unix(sock *s, char *name)
-{
-  struct sockaddr_un sa;
-  int fd;
-
-  /* We are sloppy during error (leak fd and not set s->err), but we die anyway */
-
-  fd = socket(AF_UNIX, SOCK_STREAM, 0);
-  if (fd < 0)
-    return -1;
-
-  if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
-    return -1;
-
-  /* Path length checked in test_old_bird() but we may need unix sockets for other reasons in future */
-  ASSERT_DIE(strlen(name) < sizeof(sa.sun_path));
-
-  sa.sun_family = AF_UNIX;
-  strcpy(sa.sun_path, name);
-
-  if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0)
-    return -1;
-
-  if (listen(fd, 8) < 0)
-    return -1;
-
-  s->fd = fd;
-  sk_insert(s);
-  return 0;
-}
-
 static void
 sk_reloop_hook(void *_vs)
 {
@@ -1971,6 +2009,20 @@ sk_write_noflush(sock *s)
       return 0;
     }
 
+  case SK_UNIX_ACTIVE:
+    {
+      struct sockaddr_un un;
+      un.sun_family = AF_UNIX;
+      strcpy(un.sun_path, s->host);
+
+      if (connect(s->fd, (struct sockaddr *) &un, SUN_LEN(&un)) >= 0 || errno == EISCONN)
+       sk_unix_connected(s);
+      else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS)
+       s->err_hook(s, errno);
+
+      return 0;
+    }
+
 #ifdef HAVE_LIBSSH
   case SK_SSH_ACTIVE:
     {
@@ -2017,6 +2069,9 @@ int sk_is_ipv4(sock *s)
 int sk_is_ipv6(sock *s)
 { return s->af == AF_INET6; }
 
+int sk_is_unix(sock *s)
+{ return s->af == AF_UNIX; }
+
 void
 sk_err(sock *s, int revents)
 {
index bf9f2be02b0dfe619cf88c80282746c0f7cf0ce1..f97bca746a38146e543edd2258d07fc3d644b970 100644 (file)
@@ -515,6 +515,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid)
   cli_init();
   s = cli_sk = sk_new(cli_pool);
   s->type = SK_UNIX_PASSIVE;
+  s->host = path_control_socket;
   s->rx_hook = cli_connect;
   s->err_hook = cli_connect_err;
   s->rbsize = 1024;
@@ -523,7 +524,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid)
   /* Return value intentionally ignored */
   unlink(path_control_socket);
 
-  if (sk_open_unix(s, path_control_socket) < 0)
+  if (sk_open(s) < 0)
     die("Cannot create control socket %s: %m", path_control_socket);
 
   if (use_uid || use_gid)
index ad85d1ea5fd6dd4849acda381e32202e04525894..1ab0b0792e4332c27ec7e9da0eccd23715f5f569 100644 (file)
@@ -53,7 +53,6 @@ typedef struct sockaddr_bird {
 } sockaddr;
 
 
-
 /* This is sloppy hack, it should be detected by configure script */
 /* Linux systems have it defined so this is definition for BSD systems */
 #ifndef s6_addr32
@@ -107,7 +106,6 @@ extern volatile sig_atomic_t async_shutdown_flag;
 void io_init(void);
 void io_loop(void);
 void io_log_dump(void);
-int sk_open_unix(struct birdsock *s, char *name);
 struct rfile *rf_open(struct pool *, const char *name, const char *mode);
 void *rf_file(struct rfile *f);
 int rf_fileno(struct rfile *f);