summaryrefslogtreecommitdiff
path: root/sysdep/linux
diff options
context:
space:
mode:
authorOndrej Zajicek <santiago@crfreenet.org>2014-05-18 11:42:26 +0200
committerOndrej Zajicek <santiago@crfreenet.org>2014-05-18 11:42:26 +0200
commit05476c4d04a24bdb26fa64e05ab31bc36118f34e (patch)
treee775f059cfb4bb027c444bb53eb9356e643082c8 /sysdep/linux
parent1149aa977d906a6400f998d5f6600871584395d0 (diff)
IPv4/IPv6 integrated socket code.
Diffstat (limited to 'sysdep/linux')
-rw-r--r--sysdep/linux/netlink.c8
-rw-r--r--sysdep/linux/sysio.h354
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;
}
+