diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2010-02-11 10:23:35 +0100 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2010-02-11 10:23:35 +0100 |
commit | 353729f513aa6a1f9e7f66083a0f9d2117fe2be5 (patch) | |
tree | 2ce53e3f1b1eb40b852de490db8a65eb681fadfd /sysdep | |
parent | 75f8861898d53f43cb23dbba9c776bce223c18c8 (diff) |
Temporary OSPF commit - socket changes.
Diffstat (limited to 'sysdep')
-rw-r--r-- | sysdep/bsd/sysio.h | 110 | ||||
-rw-r--r-- | sysdep/linux/sysio.h | 87 | ||||
-rw-r--r-- | sysdep/unix/io.c | 143 |
3 files changed, 319 insertions, 21 deletions
diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index 80f8f942..9ee4ade3 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -11,21 +11,39 @@ static inline void set_inaddr(struct in6_addr * ia, ip_addr a) { - ipa_hton(a); - memcpy(ia, &a, sizeof(a)); + ipa_hton(a); + memcpy(ia, &a, sizeof(a)); +} + +static inline void +get_inaddr(ip_addr *a, struct in6_addr *ia) +{ + memcpy(a, ia, sizeof(*a)); + ipa_ntoh(*a); } #else #include <net/if.h> +#include <net/if_dl.h> static inline void set_inaddr(struct in_addr * ia, ip_addr a) { - ipa_hton(a); - memcpy(&ia->s_addr, &a, sizeof(a)); + ipa_hton(a); + memcpy(&ia->s_addr, &a, sizeof(a)); +} + +static inline void +get_inaddr(ip_addr *a, struct in_addr *ia) +{ + memcpy(a, &ia->s_addr, sizeof(*a)); + ipa_ntoh(*a); } + +/* BSD Multicast handling for IPv4 */ + static inline char * sysio_setup_multicast(sock *s) { @@ -80,6 +98,90 @@ sysio_leave_group(sock *s, ip_addr maddr) return NULL; } + +/* BSD RX/TX packet info handling for IPv4 */ +/* it uses IP_RECVDSTADDR / IP_RECVIF socket options instead of IP_PKTINFO */ + +#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))) +#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_addr)) + +static char * +sysio_register_cmsgs(sock *s) +{ + int ok = 1; + if (s->flags & SKF_LADDR_RX) + { + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVDSTADDR, &ok, sizeof(ok)) < 0) + return "IP_RECVDSTADDR"; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVIF, &ok, sizeof(ok)) < 0) + return "IP_RECVIF"; + } + + return NULL; +} + +static void +sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + + if (!(s->flags & SKF_LADDR_RX)) + return; + + s->laddr = IPA_NONE; + s->lifindex = 0; + + for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVDSTADDR) + { + struct in_addr *ra = (struct in_addr *) CMSG_DATA(cm); + get_inaddr(&s->laddr, ra); + } + + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVIF) + { + struct sockaddr_dl *ri = (struct sockaddr_dl *) CMSG_DATA(cm); + s->lifindex = ri->sdl_index; + } + } + + // log(L_WARN "RX %I %d", s->laddr, s->lifindex); +} + + +void +sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + struct in_addr *sa; + + if (!(s->flags & SKF_LADDR_TX)) + { + msg->msg_controllen = 0; + return; + } + + if (s->iface) + { + struct in_addr m; +// set_inaddr(&m, s->iface->addr->ip); + 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; + cm->cmsg_len = CMSG_LEN(sizeof(*sa)); + + sa = (struct in_addr *) CMSG_DATA(cm); + set_inaddr(sa, s->saddr); + + msg->msg_controllen = cm->cmsg_len; +} + #endif diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index 70d35ccf..07f2cb15 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -21,6 +21,13 @@ set_inaddr(struct in6_addr *ia, ip_addr a) memcpy(ia, &a, sizeof(a)); } +static inline void +get_inaddr(ip_addr *a, struct in6_addr *ia) +{ + memcpy(a, ia, sizeof(*a)); + ipa_ntoh(*a); +} + #else #include <net/if.h> @@ -32,6 +39,13 @@ set_inaddr(struct in_addr *ia, ip_addr a) memcpy(&ia->s_addr, &a, sizeof(a)); } +static inline void +get_inaddr(ip_addr *a, struct in_addr *ia) +{ + memcpy(a, &ia->s_addr, sizeof(*a)); + ipa_ntoh(*a); +} + /* * Multicasting in Linux systems is a real mess. Not only different kernels * have different interfaces, but also different libc's export it in different @@ -209,3 +223,76 @@ sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) return rv; } + + +#ifndef IPV6 + +/* RX/TX packet info handling for IPv4 */ +/* Mostly similar to standardized IPv6 code */ + +#define CMSG_RX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo)) +#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo)) + +static char * +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) + return "IP_PKTINFO"; + + return NULL; +} + +static void +sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + struct in_pktinfo *pi = NULL; + + if (!(s->flags & SKF_LADDR_RX)) + return; + + for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_PKTINFO) + pi = (struct in_pktinfo *) CMSG_DATA(cm); + } + + if (!pi) + { + s->laddr = IPA_NONE; + s->lifindex = 0; + return; + } + + get_inaddr(&s->laddr, &pi->ipi_addr); + s->lifindex = pi->ipi_ifindex; + return; +} + + +void +sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + struct in_pktinfo *pi; + + if (!(s->flags & SKF_LADDR_TX)) + { + msg->msg_controllen = 0; + return; + } + + cm = CMSG_FIRSTHDR(msg); + cm->cmsg_level = IPPROTO_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; + + msg->msg_controllen = cm->cmsg_len; +} +#endif diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 296b6b3a..316244d7 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -682,7 +682,7 @@ static char * sk_setup(sock *s) { int fd = s->fd; - char *err; + char *err = NULL; if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) ERR("fcntl(O_NONBLOCK)"); @@ -701,9 +701,8 @@ sk_setup(sock *s) if (s->ttl >= 0) err = sk_set_ttl_int(s); - else - err = NULL; + sysio_register_cmsgs(s); bad: return err; } @@ -854,6 +853,72 @@ sk_leave_group(sock *s, ip_addr maddr) return 0; } +/* PKTINFO handling is also standardized in IPv6 */ +#define CMSG_RX_SPACE CMSG_SPACE(sizeof(struct in6_pktinfo)) + +static char * +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) + return "IPV6_RECVPKTINFO"; + + return NULL; +} + +void +sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + struct in6_pktinfo *pi = NULL; + + if (!(s->flags & SKF_LADDR_RX)) + return; + + for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_PKTINFO) + pi = (struct in6_pktinfo *) CMSG_DATA(cm); + } + + if (!pi) + { + s->laddr = IPA_NONE; + s->lifindex = 0; + return; + } + + get_inaddr(&s->laddr, &pi->ipi6_addr); + s->lifindex = pi->ipi6_ifindex; + return; +} + +void +sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + struct in6_pktinfo *pi; + + if (!(s->flags & SKF_LADDR_TX)) + { + msg->msg_controllen = 0; + return; + } + + cm = CMSG_FIRSTHDR(msg); + cm->cmsg_level = IPPROTO_IPV6; + cm->cmsg_type = PIV6_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; + + msg->msg_controllen = cmsg->cmsg_len; + return; +} + #else /* IPV4 */ int @@ -1107,6 +1172,8 @@ bad: return -1; } +static inline void reset_tx_buffer(sock *s) { s->ttx = s->tpos = s->tbuf; } + static int sk_maybe_write(sock *s) { @@ -1124,7 +1191,7 @@ sk_maybe_write(sock *s) { if (errno != EINTR && errno != EAGAIN) { - s->ttx = s->tpos; /* empty tx buffer */ + reset_tx_buffer(s); s->err_hook(s, errno); return -1; } @@ -1132,30 +1199,44 @@ sk_maybe_write(sock *s) } s->ttx += e; } - s->ttx = s->tpos = s->tbuf; + reset_tx_buffer(s); return 1; case SK_UDP: case SK_IP: { - sockaddr sa; - if (s->tbuf == s->tpos) return 1; - fill_in_sockaddr(&sa, s->faddr, s->fport); + sockaddr sa; + fill_in_sockaddr(&sa, s->daddr, s->dport); fill_in_sockifa(&sa, s->iface); - e = sendto(s->fd, s->tbuf, s->tpos - s->tbuf, 0, (struct sockaddr *) &sa, sizeof(sa)); + + 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, + .msg_control = cmsg_buf, + .msg_controllen = sizeof(cmsg_buf), + .msg_flags = 0}; + + sysio_prepare_tx_cmsgs(s, &msg); + e = sendmsg(s->fd, &msg, 0); + if (e < 0) { if (errno != EINTR && errno != EAGAIN) { - s->ttx = s->tpos; /* empty tx buffer */ + reset_tx_buffer(s); s->err_hook(s, errno); return -1; } return 0; } - s->tpos = s->tbuf; + reset_tx_buffer(s); return 1; } default: @@ -1201,8 +1282,6 @@ sk_rx_ready(sock *s) int sk_send(sock *s, unsigned len) { - s->faddr = s->daddr; - s->fport = s->dport; s->ttx = s->tbuf; s->tpos = s->tbuf + len; return sk_maybe_write(s); @@ -1221,13 +1300,28 @@ sk_send(sock *s, unsigned len) int sk_send_to(sock *s, unsigned len, ip_addr addr, unsigned port) { - s->faddr = addr; - s->fport = port; + s->daddr = addr; + s->dport = port; s->ttx = s->tbuf; s->tpos = s->tbuf + len; return sk_maybe_write(s); } +/* +int +sk_send_full(sock *s, unsigned len, struct iface *ifa, + ip_addr saddr, ip_addr daddr, unsigned dport) +{ + s->iface = ifa; + s->saddr = saddr; + s->daddr = daddr; + s->dport = dport; + s->ttx = s->tbuf; + s->tpos = s->tbuf + len; + return sk_maybe_write(s); +} +*/ + static int sk_read(sock *s) { @@ -1273,8 +1367,21 @@ sk_read(sock *s) default: { sockaddr sa; - int al = sizeof(sa); - int e = recvfrom(s->fd, s->rbuf, s->rbsize, 0, (struct sockaddr *) &sa, &al); + 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); if (e < 0) { @@ -1284,6 +1391,8 @@ sk_read(sock *s) } s->rpos = s->rbuf + e; get_sockaddr(&sa, &s->faddr, &s->fport, 1); + sysio_process_rx_cmsgs(s, &msg); + s->rx_hook(s, e); return 1; } |