diff options
author | Ondrej Zajicek <santiago@crfreenet.org> | 2014-05-18 11:42:26 +0200 |
---|---|---|
committer | Ondrej Zajicek <santiago@crfreenet.org> | 2014-05-18 11:42:26 +0200 |
commit | 05476c4d04a24bdb26fa64e05ab31bc36118f34e (patch) | |
tree | e775f059cfb4bb027c444bb53eb9356e643082c8 /sysdep/linux | |
parent | 1149aa977d906a6400f998d5f6600871584395d0 (diff) |
IPv4/IPv6 integrated socket code.
Diffstat (limited to 'sysdep/linux')
-rw-r--r-- | sysdep/linux/netlink.c | 8 | ||||
-rw-r--r-- | sysdep/linux/sysio.h | 354 |
2 files changed, 154 insertions, 208 deletions
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 7063e2ca..a0f85186 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -104,9 +104,9 @@ nl_request_dump(int cmd) req.nh.nlmsg_type = cmd; req.nh.nlmsg_len = sizeof(req); req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - /* Is it important which PF_* is used for link-level interface scan? - It seems that some information is available only when PF_INET is used. */ - req.g.rtgen_family = (cmd == RTM_GETLINK) ? PF_INET : BIRD_PF; + /* Is it important which AF_* is used for link-level interface scan? + It seems that some information is available only when AF_INET is used. */ + req.g.rtgen_family = (cmd == RTM_GETLINK) ? AF_INET : BIRD_AF; nl_send(&nl_scan, &req.nh); } @@ -1069,7 +1069,7 @@ nl_open_async(void) sk->type = SK_MAGIC; sk->rx_hook = nl_async_hook; sk->fd = fd; - if (sk_open(sk)) + if (sk_open(sk) < 0) bug("Netlink: sk_open failed"); } diff --git a/sysdep/linux/sysio.h b/sysdep/linux/sysio.h index dc807392..5fd75c90 100644 --- a/sysdep/linux/sysio.h +++ b/sysdep/linux/sysio.h @@ -6,232 +6,151 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -#include <net/if.h> - -#ifdef IPV6 - -#ifndef IPV6_UNICAST_HOPS -/* Needed on glibc 2.0 systems */ -#include <linux/in6.h> -#define CONFIG_IPV6_GLIBC_20 -#endif - -static inline void -set_inaddr(struct in6_addr *ia, ip_addr 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 - -static inline void -set_inaddr(struct in_addr *ia, ip_addr 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); -} - #ifndef HAVE_STRUCT_IP_MREQN /* Several versions of glibc don't define this structure, so we have to do it ourselves */ struct ip_mreqn { - struct in_addr imr_multiaddr; /* IP multicast address of group */ - struct in_addr imr_address; /* local IP address of interface */ - int imr_ifindex; /* Interface index */ + struct in_addr imr_multiaddr; /* IP multicast address of group */ + struct in_addr imr_address; /* local IP address of interface */ + int imr_ifindex; /* Interface index */ }; #endif +#ifndef IP_MINTTL +#define IP_MINTTL 21 +#endif -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_multiaddr, maddr); -} +#ifndef IPV6_TCLASS +#define IPV6_TCLASS 67 +#endif -static inline char * -sysio_setup_multicast(sock *s) -{ - struct ip_mreqn m; - int zero = 0; +#ifndef IPV6_MINHOPCOUNT +#define IPV6_MINHOPCOUNT 73 +#endif - if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - return "IP_MULTICAST_LOOP"; - if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0) - return "IP_MULTICAST_TTL"; +#ifndef TCP_MD5SIG - /* This defines where should we send _outgoing_ multicasts */ - fill_mreqn(&m, IPA_NONE, s->iface); - if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &m, sizeof(m)) < 0) - return "IP_MULTICAST_IF"; +#define TCP_MD5SIG 14 +#define TCP_MD5SIG_MAXKEYLEN 80 - return NULL; -} +struct tcp_md5sig { + struct sockaddr_storage tcpm_addr; /* address associated */ + u16 __tcpm_pad1; /* zero */ + u16 tcpm_keylen; /* key length */ + u32 __tcpm_pad2; /* zero */ + u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ +}; -static inline char * -sysio_join_group(sock *s, ip_addr maddr) -{ - struct ip_mreqn m; +#endif - /* And this one sets interface for _receiving_ multicasts from */ - fill_mreqn(&m, maddr, s->iface); - if (setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &m, sizeof(m)) < 0) - return "IP_ADD_MEMBERSHIP"; - return NULL; -} +/* Linux does not care if sa_len is larger than needed */ +#define SA_LEN(x) sizeof(sockaddr) -static inline char * -sysio_leave_group(sock *s, ip_addr maddr) -{ - struct ip_mreqn m; - /* And this one sets interface for _receiving_ multicasts from */ - fill_mreqn(&m, maddr, s->iface); - if (setsockopt(s->fd, SOL_IP, IP_DROP_MEMBERSHIP, &m, sizeof(m)) < 0) - return "IP_DROP_MEMBERSHIP"; +/* + * Linux IPv4 multicast syscalls + */ - return NULL; -} +#define INIT_MREQ4(maddr,ifa) \ + { .imr_multiaddr = ipa_to_in4(maddr), .imr_ifindex = ifa->index } -#endif +static inline int +sk_setup_multicast4(sock *s) +{ + struct ip_mreqn mr = { .imr_ifindex = s->iface->index }; + int ttl = s->ttl; + int n = 0; + /* This defines where should we send _outgoing_ multicasts */ + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mr, sizeof(mr)) < 0) + ERR("IP_MULTICAST_IF"); -/* For the case that we have older libc headers */ -/* Copied from Linux kernel file include/linux/tcp.h */ + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + ERR("IP_MULTICAST_TTL"); -#ifndef TCP_MD5SIG + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &n, sizeof(n)) < 0) + ERR("IP_MULTICAST_LOOP"); -#define TCP_MD5SIG 14 -#define TCP_MD5SIG_MAXKEYLEN 80 + return 0; +} -#include <linux/types.h> +static inline int +sk_join_group4(sock *s, ip_addr maddr) +{ + struct ip_mreqn mr = INIT_MREQ4(maddr, s->iface); -struct tcp_md5sig { - struct sockaddr_storage tcpm_addr; /* address associated */ - __u16 __tcpm_pad1; /* zero */ - __u16 tcpm_keylen; /* key length */ - __u32 __tcpm_pad2; /* zero */ - __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ -}; + if (setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) + ERR("IP_ADD_MEMBERSHIP"); -#endif + return 0; +} -static int -sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) +static inline int +sk_leave_group4(sock *s, ip_addr maddr) { - struct tcp_md5sig md5; + struct ip_mreqn mr = INIT_MREQ4(maddr, s->iface); - memset(&md5, 0, sizeof(md5)); - memcpy(&md5.tcpm_addr, (struct sockaddr *) sa, sizeof(*sa)); + if (setsockopt(s->fd, SOL_IP, IP_DROP_MEMBERSHIP, &mr, sizeof(mr)) < 0) + ERR("IP_DROP_MEMBERSHIP"); - if (passwd) - { - int len = strlen(passwd); - - if (len > TCP_MD5SIG_MAXKEYLEN) - { - log(L_ERR "MD5 password too long"); - return -1; - } - - md5.tcpm_keylen = len; - memcpy(&md5.tcpm_key, passwd, len); - } - - int rv = setsockopt(s->fd, SOL_TCP, TCP_MD5SIG, &md5, sizeof(md5)); - - if (rv < 0) - { - if (errno == ENOPROTOOPT) - log(L_ERR "Kernel does not support TCP MD5 signatures"); - else - log(L_ERR "sk_set_md5_auth_int: setsockopt: %m"); - } - - return rv; + return 0; } -#ifndef IPV6 +/* + * Linux IPv4 packet control messages + */ -/* RX/TX packet info handling for IPv4 */ /* Mostly similar to standardized IPv6 code */ -#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in_pktinfo)) + CMSG_SPACE(sizeof(int))) -#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in_pktinfo)) +#define CMSG4_SPACE_PKTINFO CMSG_SPACE(sizeof(struct in_pktinfo)) +#define CMSG4_SPACE_TTL CMSG_SPACE(sizeof(int)) -static char * -sysio_register_cmsgs(sock *s) +static inline int +sk_request_cmsg4_pktinfo(sock *s) { - int ok = 1; + int y = 1; - if ((s->flags & SKF_LADDR_RX) && - (setsockopt(s->fd, SOL_IP, IP_PKTINFO, &ok, sizeof(ok)) < 0)) - return "IP_PKTINFO"; + if (setsockopt(s->fd, SOL_IP, IP_PKTINFO, &y, sizeof(y)) < 0) + ERR("IP_PKTINFO"); - if ((s->flags & SKF_TTL_RX) && - (setsockopt(s->fd, SOL_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0)) - return "IP_RECVTTL"; - - return NULL; + return 0; } -static void -sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) +static inline int +sk_request_cmsg4_ttl(sock *s) { - struct cmsghdr *cm; - struct in_pktinfo *pi = NULL; - int *ttl = NULL; + int y = 1; - for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) - { - if (cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_PKTINFO) - pi = (struct in_pktinfo *) CMSG_DATA(cm); + if (setsockopt(s->fd, SOL_IP, IP_RECVTTL, &y, sizeof(y)) < 0) + ERR("IP_RECVTTL"); - if (cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_TTL) - ttl = (int *) CMSG_DATA(cm); - } + return 0; +} - if (s->flags & SKF_LADDR_RX) +static inline void +sk_process_cmsg4_pktinfo(sock *s, struct cmsghdr *cm) +{ + if (cm->cmsg_type == IP_PKTINFO) { - if (pi) - { - get_inaddr(&s->laddr, &pi->ipi_addr); - s->lifindex = pi->ipi_ifindex; - } - else - { - s->laddr = IPA_NONE; - s->lifindex = 0; - } + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cm); + s->laddr = ipa_from_in4(pi->ipi_addr); + s->lifindex = pi->ipi_ifindex; } +} - if (s->flags & SKF_TTL_RX) - s->ttl = ttl ? *ttl : -1; - - return; +static inline void +sk_process_cmsg4_ttl(sock *s, struct cmsghdr *cm) +{ + if (cm->cmsg_type == IP_TTL) + s->rcv_ttl = * (int *) CMSG_DATA(cm); } -static void -sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) +static inline void +sk_prepare_cmsgs4(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) { struct cmsghdr *cm; struct in_pktinfo *pi; @@ -246,78 +165,105 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) pi = (struct in_pktinfo *) CMSG_DATA(cm); pi->ipi_ifindex = s->iface ? s->iface->index : 0; - set_inaddr(&pi->ipi_spec_dst, s->saddr); - set_inaddr(&pi->ipi_addr, IPA_NONE); + pi->ipi_spec_dst = ipa_to_in4(s->saddr); + pi->ipi_addr = ipa_to_in4(IPA_NONE); msg->msg_controllen = cm->cmsg_len; } -#endif +/* + * Miscellaneous Linux socket syscalls + */ +int +sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) +{ + struct tcp_md5sig md5; -#ifndef IP_MINTTL -#define IP_MINTTL 21 -#endif + memset(&md5, 0, sizeof(md5)); + sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, a, ifa, 0); -#ifndef IPV6_MINHOPCOUNT -#define IPV6_MINHOPCOUNT 73 -#endif + if (passwd) + { + int len = strlen(passwd); + + if (len > TCP_MD5SIG_MAXKEYLEN) + ERR_MSG("MD5 password too long"); + md5.tcpm_keylen = len; + memcpy(&md5.tcpm_key, passwd, len); + } + + if (setsockopt(s->fd, SOL_TCP, TCP_MD5SIG, &md5, sizeof(md5)) < 0) + { + if (errno == ENOPROTOOPT) + ERR_MSG("Kernel does not support TCP MD5 signatures"); + else + ERR("TCP_MD5SIG"); + } -#ifndef IPV6 + return 0; +} -static int +static inline int sk_set_min_ttl4(sock *s, int ttl) { 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"); + ERR_MSG("Kernel does not support IPv4 TTL security"); else - log(L_ERR "sk_set_min_ttl4: setsockopt: %m"); - - return -1; + ERR("IP_MINTTL"); } return 0; } -#else - -static int +static inline int sk_set_min_ttl6(sock *s, int ttl) { 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"); + ERR_MSG("Kernel does not support IPv6 TTL security"); else - log(L_ERR "sk_set_min_ttl6: setsockopt: %m"); - - return -1; + ERR("IPV6_MINHOPCOUNT"); } return 0; } -#endif +static inline int +sk_disable_mtu_disc4(sock *s) +{ + int dont = IP_PMTUDISC_DONT; + if (setsockopt(s->fd, SOL_IP, IP_MTU_DISCOVER, &dont, sizeof(dont)) < 0) + ERR("IP_MTU_DISCOVER"); -#ifndef IPV6_TCLASS -#define IPV6_TCLASS 67 -#endif + return 0; +} + +static inline int +sk_disable_mtu_disc6(sock *s) +{ + int dont = IPV6_PMTUDISC_DONT; + + if (setsockopt(s->fd, SOL_IPV6, IPV6_MTU_DISCOVER, &dont, sizeof(dont)) < 0) + ERR("IPV6_MTU_DISCOVER"); + + return 0; +} int sk_priority_control = 7; -static int +static inline int sk_set_priority(sock *s, int prio) { if (setsockopt(s->fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0) - { - log(L_WARN "sk_set_priority: setsockopt: %m"); - return -1; - } + ERR("SO_PRIORITY"); return 0; } + |