diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2014-02-06 17:46:01 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2014-02-06 17:46:01 +0100 |
commit | 48e5f32db676645640f84ab3d630cce975aa6b20 (patch) | |
tree | b940fc8156b3e0c18aab6c339a066bdb7a9ec1e0 /sysdep | |
parent | f48fa14214301382b2e6b134788a7506b61b664f (diff) |
Many changes in I/O and OSPF sockets and packet handling.
I/O:
- BSD: specify src addr on IP sockets by IP_HDRINCL
- BSD: specify src addr on UDP sockets by IP_SENDSRCADDR
- Linux: specify src addr on IP/UDP sockets by IP_PKTINFO
- IPv6: specify src addr on IP/UDP sockets by IPV6_PKTINFO
- Alternative SKF_BIND flag for binding to IP address
- Allows IP/UDP sockets without tx_hook, on these
sockets a packet is discarded when TX queue is full
- Use consistently SOL_ for socket layer values.
OSPF:
- Packet src addr is always explicitly set
- Support for secondary addresses in BSD
- Dynamic RX/TX buffers
- Fixes some minor buffer overruns
- Interface option 'tx length'
- Names for vlink pseudoifaces (vlinkX)
- Vlinks use separate socket for TX
- Vlinks do not use fixed associated iface
- Fixes TTL for direct unicast packets
- Fixes DONTROUTE for OSPF sockets
- Use ifa->ifname instead of ifa->iface->name
Diffstat (limited to 'sysdep')
-rw-r--r-- | sysdep/bsd/sysio.h | 53 | ||||
-rw-r--r-- | sysdep/cf/README | 2 | ||||
-rw-r--r-- | sysdep/cf/bsd.h | 1 | ||||
-rw-r--r-- | sysdep/linux/sysio.h | 56 | ||||
-rw-r--r-- | sysdep/unix/io.c | 294 |
5 files changed, 256 insertions, 150 deletions
diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index cf049a0b..e45deb6f 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -38,18 +38,13 @@ get_inaddr(ip_addr *a, struct in6_addr *ia) ipa_ntoh(*a); } -static inline char * -sysio_bind_to_iface(sock *s) -{ - /* Unfortunately not available */ - return NULL; -} - #else #include <net/if.h> #include <net/if_dl.h> +#include <netinet/in_systm.h> // Workaround for some BSDs +#include <netinet/ip.h> static inline void set_inaddr(struct in_addr * ia, ip_addr a) @@ -93,7 +88,7 @@ sysio_setup_multicast(sock *s) static inline char * sysio_join_group(sock *s, ip_addr maddr) { - struct ip_mreq mreq; + struct ip_mreq mreq; bzero(&mreq, sizeof(mreq)); set_inaddr(&mreq.imr_interface, s->iface->addr->ip); @@ -152,7 +147,7 @@ sysio_register_cmsgs(sock *s) return NULL; } -static void +static inline void sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) { struct cmsghdr *cm; @@ -190,26 +185,17 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) } /* Unfortunately, IP_SENDSRCADDR does not work for raw IP sockets on BSD kernels */ -/* -static void + +static inline void sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) { +#ifdef IP_SENDSRCADDR struct cmsghdr *cm; struct in_addr *sa; - if (!(s->flags & SKF_LADDR_TX)) - return; - msg->msg_control = cbuf; msg->msg_controllen = cbuflen; - if (s->iface) - { - struct in_addr m; - set_inaddr(&m, s->saddr); - setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_IF, &m, sizeof(m)); - } - cm = CMSG_FIRSTHDR(msg); cm->cmsg_level = IPPROTO_IP; cm->cmsg_type = IP_SENDSRCADDR; @@ -219,8 +205,31 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) set_inaddr(sa, s->saddr); msg->msg_controllen = cm->cmsg_len; +#endif +} + + +static void +fill_ip_header(sock *s, void *hdr, int dlen) +{ + struct ip *ip = hdr; + + bzero(ip, 20); + + ip->ip_v = 4; + ip->ip_hl = 5; + ip->ip_tos = (s->tos < 0) ? 0 : s->tos; + ip->ip_len = 20 + dlen; + ip->ip_ttl = (s->ttl < 0) ? 64 : s->ttl; + ip->ip_p = s->dport; + set_inaddr(&ip->ip_src, s->saddr); + set_inaddr(&ip->ip_dst, s->daddr); + +#ifdef __OpenBSD__ + /* OpenBSD expects ip_len in network order, other BSDs expect host order */ + ip->ip_len = htons(ip->ip_len); +#endif } -*/ #endif diff --git a/sysdep/cf/README b/sysdep/cf/README index 1c11edcf..768a3727 100644 --- a/sysdep/cf/README +++ b/sysdep/cf/README @@ -8,6 +8,8 @@ CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once CONFIG_MC_PROPER_SRC Multicast packets have source address according to socket saddr field CONFIG_SKIP_MC_BIND Don't call bind on multicast socket (def for *BSD) +CONFIG_NO_IFACE_BIND Bind to iface is not available, use workarounds (def for *BSD) CONFIG_UNIX_DONTROUTE Use setsockopts DONTROUTE (undef for *BSD) +CONFIG_USE_HDRINCL Use IP_HDRINCL instead of control messages for source address on raw IP sockets. CONFIG_RESTRICTED_PRIVILEGES Implements restricted privileges using drop_uid() diff --git a/sysdep/cf/bsd.h b/sysdep/cf/bsd.h index 5e6d03e8..df199199 100644 --- a/sysdep/cf/bsd.h +++ b/sysdep/cf/bsd.h @@ -12,6 +12,7 @@ #define CONFIG_SKIP_MC_BIND #define CONFIG_NO_IFACE_BIND +#define CONFIG_USE_HDRINCL /* Link: sysdep/unix diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 250ed586..56c3387d 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -30,17 +30,6 @@ get_inaddr(ip_addr *a, struct in6_addr *ia) ipa_ntoh(*a); } -static inline char * -sysio_bind_to_iface(sock *s) -{ - struct ifreq ifr; - strcpy(ifr.ifr_name, s->iface->name); - if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) - return "SO_BINDTODEVICE"; - - return NULL; -} - #else static inline void @@ -69,11 +58,10 @@ struct ip_mreqn #endif -static inline void fill_mreqn(struct ip_mreqn *m, struct iface *ifa, ip_addr saddr, ip_addr maddr) +static inline void fill_mreqn(struct ip_mreqn *m, ip_addr maddr, struct iface *ifa) { bzero(m, sizeof(*m)); m->imr_ifindex = ifa->index; - set_inaddr(&m->imr_address, saddr); set_inaddr(&m->imr_multiaddr, maddr); } @@ -90,16 +78,10 @@ sysio_setup_multicast(sock *s) return "IP_MULTICAST_TTL"; /* This defines where should we send _outgoing_ multicasts */ - fill_mreqn(&m, s->iface, s->saddr, IPA_NONE); + fill_mreqn(&m, IPA_NONE, s->iface); if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &m, sizeof(m)) < 0) return "IP_MULTICAST_IF"; - /* Is this necessary? */ - struct ifreq ifr; - strcpy(ifr.ifr_name, s->iface->name); - if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) - return "SO_BINDTODEVICE"; - return NULL; } @@ -109,7 +91,7 @@ sysio_join_group(sock *s, ip_addr maddr) struct ip_mreqn m; /* And this one sets interface for _receiving_ multicasts from */ - fill_mreqn(&m, s->iface, s->saddr, maddr); + fill_mreqn(&m, maddr, s->iface); if (setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &m, sizeof(m)) < 0) return "IP_ADD_MEMBERSHIP"; @@ -122,7 +104,7 @@ sysio_leave_group(sock *s, ip_addr maddr) struct ip_mreqn m; /* And this one sets interface for _receiving_ multicasts from */ - fill_mreqn(&m, s->iface, s->saddr, maddr); + fill_mreqn(&m, maddr, s->iface); if (setsockopt(s->fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0) return "IP_DROP_MEMBERSHIP"; @@ -132,10 +114,7 @@ sysio_leave_group(sock *s, ip_addr maddr) #endif -#include <linux/socket.h> -#include <linux/tcp.h> - -/* For the case that we have older kernel headers */ +/* For the case that we have older libc headers */ /* Copied from Linux kernel file include/linux/tcp.h */ #ifndef TCP_MD5SIG @@ -175,7 +154,7 @@ sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) memcpy(&md5.tcpm_key, passwd, len); } - int rv = setsockopt(s->fd, IPPROTO_TCP, TCP_MD5SIG, &md5, sizeof(md5)); + int rv = setsockopt(s->fd, SOL_TCP, TCP_MD5SIG, &md5, sizeof(md5)); if (rv < 0) { @@ -203,11 +182,11 @@ sysio_register_cmsgs(sock *s) int ok = 1; if ((s->flags & SKF_LADDR_RX) && - (setsockopt(s->fd, IPPROTO_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0)) + (setsockopt(s->fd, SOL_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0)) return "IP_PKTINFO"; if ((s->flags & SKF_TTL_RX) && - (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0)) + (setsockopt(s->fd, SOL_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0)) return "IP_RECVTTL"; return NULL; @@ -222,10 +201,10 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) { - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO) + if (cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_PKTINFO) pi = (struct in_pktinfo *) CMSG_DATA(cm); - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_TTL) + if (cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_TTL) ttl = (int *) CMSG_DATA(cm); } @@ -249,31 +228,28 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) return; } -/* static void sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) { struct cmsghdr *cm; struct in_pktinfo *pi; - if (!(s->flags & SKF_LADDR_TX)) - return; - msg->msg_control = cbuf; msg->msg_controllen = cbuflen; cm = CMSG_FIRSTHDR(msg); - cm->cmsg_level = IPPROTO_IP; + cm->cmsg_level = SOL_IP; cm->cmsg_type = IP_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(*pi)); pi = (struct in_pktinfo *) CMSG_DATA(cm); - set_inaddr(&pi->ipi_spec_dst, s->saddr); pi->ipi_ifindex = s->iface ? s->iface->index : 0; + set_inaddr(&pi->ipi_spec_dst, s->saddr); + set_inaddr(&pi->ipi_addr, IPA_NONE); msg->msg_controllen = cm->cmsg_len; } -*/ + #endif @@ -292,7 +268,7 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) static int sk_set_min_ttl4(sock *s, int ttl) { - if (setsockopt(s->fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) + if (setsockopt(s->fd, SOL_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) { if (errno == ENOPROTOOPT) log(L_ERR "Kernel does not support IPv4 TTL security"); @@ -310,7 +286,7 @@ sk_set_min_ttl4(sock *s, int ttl) static int sk_set_min_ttl6(sock *s, int ttl) { - if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_MINHOPCOUNT, &ttl, sizeof(ttl)) < 0) + if (setsockopt(s->fd, SOL_IPV6, IPV6_MINHOPCOUNT, &ttl, sizeof(ttl)) < 0) { if (errno == ENOPROTOOPT) log(L_ERR "Kernel does not support IPv6 TTL security"); diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 6e3f1e4d..428f24cc 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -23,6 +23,8 @@ #include <fcntl.h> #include <errno.h> #include <netinet/in.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> #include <netinet/icmp6.h> #include "nest/bird.h" @@ -489,6 +491,11 @@ tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t) #define SOL_IPV6 IPPROTO_IPV6 #endif +#ifndef SOL_ICMPV6 +#define SOL_ICMPV6 IPPROTO_ICMPV6 +#endif + + static list sock_list; static struct birdsock *current_sock; static struct birdsock *stored_sock; @@ -553,6 +560,43 @@ sk_free(resource *r) } void +sk_set_rbsize(sock *s, uint val) +{ + ASSERT(s->rbuf_alloc == s->rbuf); + + if (s->rbsize == val) + return; + + s->rbsize = val; + xfree(s->rbuf_alloc); + s->rbuf_alloc = xmalloc(val); + s->rpos = s->rbuf = s->rbuf_alloc; +} + +void +sk_set_tbsize(sock *s, uint val) +{ + ASSERT(s->tbuf_alloc == s->tbuf); + + if (s->tbsize == val) + return; + + byte *old_tbuf = s->tbuf; + + s->tbsize = val; + s->tbuf = s->tbuf_alloc = xrealloc(s->tbuf_alloc, val); + s->tpos = s->tbuf + (s->tpos - old_tbuf); + s->ttx = s->tbuf + (s->ttx - old_tbuf); +} + +void +sk_set_tbuf(sock *s, void *tbuf) +{ + s->tbuf = tbuf ?: s->tbuf_alloc; + s->ttx = s->tpos = s->tbuf; +} + +void sk_reallocate(sock *s) { sk_free_bufs(s); @@ -703,11 +747,11 @@ sysio_register_cmsgs(sock *s) int ok = 1; if ((s->flags & SKF_LADDR_RX) && - (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0)) + (setsockopt(s->fd, SOL_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0)) return "IPV6_RECVPKTINFO"; if ((s->flags & SKF_TTL_RX) && - (setsockopt(s->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &ok, sizeof(ok)) < 0)) + (setsockopt(s->fd, SOL_IPV6, IPV6_RECVHOPLIMIT, &ok, sizeof(ok)) < 0)) return "IPV6_RECVHOPLIMIT"; return NULL; @@ -722,10 +766,10 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) { - if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO) + if (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_PKTINFO) pi = (struct in6_pktinfo *) CMSG_DATA(cm); - if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT) + if (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT) hlim = (int *) CMSG_DATA(cm); } @@ -749,32 +793,27 @@ sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) return; } -/* static void sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) { struct cmsghdr *cm; struct in6_pktinfo *pi; - if (!(s->flags & SKF_LADDR_TX)) - return; - msg->msg_control = cbuf; msg->msg_controllen = cbuflen; cm = CMSG_FIRSTHDR(msg); - cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_level = SOL_IPV6; cm->cmsg_type = IPV6_PKTINFO; cm->cmsg_len = CMSG_LEN(sizeof(*pi)); pi = (struct in6_pktinfo *) CMSG_DATA(cm); - set_inaddr(&pi->ipi6_addr, s->saddr); pi->ipi6_ifindex = s->iface ? s->iface->index : 0; + set_inaddr(&pi->ipi6_addr, s->saddr); msg->msg_controllen = cm->cmsg_len; - return; } -*/ + #endif static char * @@ -786,11 +825,6 @@ sk_set_ttl_int(sock *s) #else if (setsockopt(s->fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0) return "IP_TTL"; -#ifdef CONFIG_UNIX_DONTROUTE - int one = 1; - if (s->ttl == 1 && setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0) - return "SO_DONTROUTE"; -#endif #endif return NULL; } @@ -801,6 +835,7 @@ sk_set_ttl_int(sock *s) static char * sk_setup(sock *s) { + int one = 1; int fd = s->fd; char *err = NULL; @@ -809,6 +844,41 @@ sk_setup(sock *s) if (s->type == SK_UNIX) return NULL; + if (ipa_nonzero(s->saddr) && !(s->flags & SKF_BIND)) + s->flags |= SKF_PKTINFO; + +#ifdef CONFIG_USE_HDRINCL + if ((s->type == SK_IP) && (s->flags & SKF_PKTINFO)) + { + s->flags &= ~SKF_PKTINFO; + s->flags |= SKF_HDRINCL; + if (setsockopt(fd, SOL_IP, IP_HDRINCL, &one, sizeof(one)) < 0) + ERR("IP_HDRINCL"); + } +#endif + + if (s->iface) + { +#ifdef SO_BINDTODEVICE + struct ifreq ifr; + strcpy(ifr.ifr_name, s->iface->name); + if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) + ERR("SO_BINDTODEVICE"); +#endif + +#ifdef CONFIG_UNIX_DONTROUTE + if (setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0) + ERR("SO_DONTROUTE"); +#endif + } + + if ((s->ttl >= 0) && (err = sk_set_ttl_int(s))) + goto bad; + + if (err = sysio_register_cmsgs(s)) + goto bad; + + #ifdef IPV6 if ((s->tos >= 0) && setsockopt(fd, SOL_IPV6, IPV6_TCLASS, &s->tos, sizeof(s->tos)) < 0) WARN("IPV6_TCLASS"); @@ -821,15 +891,10 @@ sk_setup(sock *s) sk_set_priority(s, s->priority); #ifdef IPV6 - int v = 1; - if ((s->flags & SKF_V6ONLY) && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v, sizeof(v)) < 0) + if ((s->flags & SKF_V6ONLY) && setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) WARN("IPV6_V6ONLY"); #endif - if ((s->ttl >= 0) && (err = sk_set_ttl_int(s))) - goto bad; - - err = sysio_register_cmsgs(s); bad: return err; } @@ -926,7 +991,7 @@ sk_set_broadcast(sock *s, int enable) int sk_set_ipv6_checksum(sock *s, int offset) { - if (setsockopt(s->fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)) < 0) + if (setsockopt(s->fd, SOL_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)) < 0) { log(L_ERR "sk_set_ipv6_checksum: IPV6_CHECKSUM: %m"); return -1; @@ -945,7 +1010,7 @@ sk_set_icmp_filter(sock *s, int p1, int p2) ICMP6_FILTER_SETPASS(p1, &f); ICMP6_FILTER_SETPASS(p2, &f); - if (setsockopt(s->fd, IPPROTO_ICMPV6, ICMP6_FILTER, &f, sizeof(f)) < 0) + if (setsockopt(s->fd, SOL_ICMPV6, ICMP6_FILTER, &f, sizeof(f)) < 0) { log(L_ERR "sk_setup_icmp_filter: ICMP6_FILTER: %m"); return -1; @@ -961,7 +1026,7 @@ sk_setup_multicast(sock *s) int zero = 0; int index; - ASSERT(s->iface && s->iface->addr); + ASSERT(s->iface); index = s->iface->index; if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) @@ -971,9 +1036,6 @@ sk_setup_multicast(sock *s) if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0) ERR("IPV6_MULTICAST_IF"); - if (err = sysio_bind_to_iface(s)) - goto bad; - return 0; bad: @@ -981,18 +1043,17 @@ bad: return -1; } +#ifdef CONFIG_IPV6_GLIBC_20 +#define ipv6mr_interface ipv6mr_ifindex +#endif + int sk_join_group(sock *s, ip_addr maddr) { struct ipv6_mreq mreq; set_inaddr(&mreq.ipv6mr_multiaddr, maddr); - -#ifdef CONFIG_IPV6_GLIBC_20 - mreq.ipv6mr_ifindex = s->iface->index; -#else mreq.ipv6mr_interface = s->iface->index; -#endif if (setsockopt(s->fd, SOL_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) { @@ -1009,12 +1070,7 @@ sk_leave_group(sock *s, ip_addr maddr) struct ipv6_mreq mreq; set_inaddr(&mreq.ipv6mr_multiaddr, maddr); - -#ifdef CONFIG_IPV6_GLIBC_20 - mreq.ipv6mr_ifindex = s->iface->index; -#else mreq.ipv6mr_interface = s->iface->index; -#endif if (setsockopt(s->fd, SOL_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)) < 0) { @@ -1032,7 +1088,7 @@ sk_setup_multicast(sock *s) { char *err; - ASSERT(s->iface && s->iface->addr); + ASSERT(s->iface); if (err = sysio_setup_multicast(s)) { @@ -1142,31 +1198,45 @@ int sk_open(sock *s) { int fd; - sockaddr sa; int one = 1; - int type = s->type; - int has_src = ipa_nonzero(s->saddr) || s->sport; + int do_bind = 0; + int bind_port = 0; + ip_addr bind_addr = IPA_NONE; + sockaddr sa; char *err; - switch (type) + switch (s->type) { case SK_TCP_ACTIVE: s->ttx = ""; /* Force s->ttx != s->tpos */ /* Fall thru */ case SK_TCP_PASSIVE: fd = socket(BIRD_PF, SOCK_STREAM, IPPROTO_TCP); + bind_port = s->sport; + bind_addr = s->saddr; + do_bind = bind_port || ipa_nonzero(bind_addr); break; + case SK_UDP: fd = socket(BIRD_PF, SOCK_DGRAM, IPPROTO_UDP); + bind_port = s->sport; + bind_addr = (s->flags & SKF_BIND) ? s->saddr : IPA_NONE; + do_bind = 1; break; + case SK_IP: fd = socket(BIRD_PF, SOCK_RAW, s->dport); + bind_port = 0; + bind_addr = (s->flags & SKF_BIND) ? s->saddr : IPA_NONE; + do_bind = ipa_nonzero(bind_addr); break; + case SK_MAGIC: fd = s->fd; break; + default: - bug("sk_open() called for invalid sock type %d", type); + bug("sk_open() called for invalid sock type %d", s->type); } if (fd < 0) die("sk_open: socket: %m"); @@ -1175,31 +1245,28 @@ sk_open(sock *s) if (err = sk_setup(s)) goto bad; - if (has_src) + if (do_bind) { - int port; - - if (type == SK_IP) - port = 0; - else + if (bind_port) { - port = s->sport; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) ERR("SO_REUSEADDR"); - + #ifdef CONFIG_NO_IFACE_BIND /* Workaround missing ability to bind to an iface */ - if ((type == SK_UDP) && s->iface && ipa_zero(s->saddr)) + if ((s->type == SK_UDP) && s->iface && ipa_zero(bind_addr)) { if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0) ERR("SO_REUSEPORT"); } #endif } - fill_in_sockaddr(&sa, s->saddr, s->iface, port); + + fill_in_sockaddr(&sa, bind_addr, s->iface, bind_port); if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) ERR("bind"); } + fill_in_sockaddr(&sa, s->daddr, s->iface, s->dport); if (s->password) @@ -1209,7 +1276,7 @@ sk_open(sock *s) goto bad_no_log; } - switch (type) + switch (s->type) { case SK_TCP_ACTIVE: if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0) @@ -1287,6 +1354,79 @@ sk_open_unix(sock *s, char *name) die("Unable to create control socket %s", name); } + +static inline int +sk_sendmsg(sock *s) +{ + struct iovec iov = {s->tbuf, s->tpos - s->tbuf}; + byte cmsg_buf[CMSG_TX_SPACE]; + sockaddr dst; + + fill_in_sockaddr(&dst, s->daddr, s->iface, s->dport); + + struct msghdr msg = { + .msg_name = &dst, + .msg_namelen = sizeof(dst), + .msg_iov = &iov, + .msg_iovlen = 1 + }; + +#ifdef CONFIG_USE_HDRINCL + byte hdr[20]; + struct iovec iov2[2] = { {hdr, 20}, iov }; + + if (s->flags & SKF_HDRINCL) + { + fill_ip_header(s, hdr, iov.iov_len); + msg.msg_iov = iov2; + msg.msg_iovlen = 2; + } +#endif + + if (s->flags & SKF_PKTINFO) + sysio_prepare_tx_cmsgs(s, &msg, cmsg_buf, sizeof(cmsg_buf)); + + return sendmsg(s->fd, &msg, 0); +} + +static inline int +sk_recvmsg(sock *s) +{ + struct iovec iov = {s->rbuf, s->rbsize}; + byte cmsg_buf[CMSG_RX_SPACE]; + sockaddr src; + + struct msghdr msg = { + .msg_name = &src, + .msg_namelen = sizeof(src), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsg_buf, + .msg_controllen = sizeof(cmsg_buf), + .msg_flags = 0 + }; + + int rv = recvmsg(s->fd, &msg, 0); + if (rv < 0) + return rv; + + //ifdef IPV4 + // if (cf_type == SK_IP) + // rv = ipv4_skip_header(pbuf, rv); + //endif + + get_sockaddr(&src, &s->faddr, NULL, &s->fport, 1); + sysio_process_rx_cmsgs(s, &msg); + + if (msg.msg_flags & MSG_TRUNC) + s->flags |= SKF_TRUNCATED; + else + s->flags &= ~SKF_TRUNCATED; + + return rv; +} + + static inline void reset_tx_buffer(sock *s) { s->ttx = s->tpos = s->tbuf; } static int @@ -1323,20 +1463,7 @@ sk_maybe_write(sock *s) if (s->tbuf == s->tpos) return 1; - sockaddr sa; - fill_in_sockaddr(&sa, s->daddr, s->iface, s->dport); - - struct iovec iov = {s->tbuf, s->tpos - s->tbuf}; - // byte cmsg_buf[CMSG_TX_SPACE]; - - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = &iov, - .msg_iovlen = 1}; - - // sysio_prepare_tx_cmsgs(s, &msg, cmsg_buf, sizeof(cmsg_buf)); - e = sendmsg(s->fd, &msg, 0); + e = sk_sendmsg(s); if (e < 0) { @@ -1346,6 +1473,9 @@ sk_maybe_write(sock *s) s->err_hook(s, errno); return -1; } + + if (!s->tx_hook) + reset_tx_buffer(s); return 0; } reset_tx_buffer(s); @@ -1408,12 +1538,15 @@ sk_send(sock *s, unsigned len) * * This is a sk_send() replacement for connection-less packet sockets * which allows destination of the packet to be chosen dynamically. + * Raw IP sockets should use 0 for @port. */ int sk_send_to(sock *s, unsigned len, ip_addr addr, unsigned port) { s->daddr = addr; - s->dport = port; + if (port) + s->dport = port; + s->ttx = s->tbuf; s->tpos = s->tbuf + len; return sk_maybe_write(s); @@ -1480,22 +1613,9 @@ sk_read(sock *s) return s->rx_hook(s, 0); default: { - sockaddr sa; int e; - struct iovec iov = {s->rbuf, s->rbsize}; - byte cmsg_buf[CMSG_RX_SPACE]; - - struct msghdr msg = { - .msg_name = &sa, - .msg_namelen = sizeof(sa), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = cmsg_buf, - .msg_controllen = sizeof(cmsg_buf), - .msg_flags = 0}; - - e = recvmsg(s->fd, &msg, 0); + e = sk_recvmsg(s); if (e < 0) { @@ -1503,10 +1623,8 @@ sk_read(sock *s) s->err_hook(s, errno); return 0; } - s->rpos = s->rbuf + e; - get_sockaddr(&sa, &s->faddr, NULL, &s->fport, 1); - sysio_process_rx_cmsgs(s, &msg); + s->rpos = s->rbuf + e; s->rx_hook(s, e); return 1; } |