diff --git a/client/birdc.c b/client/birdc.c index 6d4c86a9..ccf758be 100644 --- a/client/birdc.c +++ b/client/birdc.c @@ -19,7 +19,6 @@ #include "lib/resource.h" #include "lib/string.h" #include "client/client.h" -#include "sysdep/unix/unix.h" static int input_hidden_end; static int prompt_active; diff --git a/lib/printf.c b/lib/printf.c index 41e1cc0d..ebecc140 100644 --- a/lib/printf.c +++ b/lib/printf.c @@ -221,6 +221,16 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args) continue; case 'm': + if (flags & SPECIAL) { + if (!errno) + continue; + if (size < 2) + return -1; + *str++ = ':'; + *str++ = ' '; + start += 2; + size -= 2; + } s = strerror(errno); goto str; case 'M': diff --git a/lib/socket.h b/lib/socket.h index 894d5561..f1fffa94 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -10,6 +10,7 @@ #define _BIRD_SOCKET_H_ #include +// #include #include "lib/resource.h" @@ -43,17 +44,21 @@ typedef struct birdsock { unsigned lifindex; /* local interface that received the datagram */ /* laddr and lifindex are valid only if SKF_LADDR_RX flag is set to request it */ + int af; /* Address family (AF_INET, AF_INET6 or 0 for non-IP) of fd */ int fd; /* System-dependent data */ int index; /* Index in poll buffer */ + int rcv_ttl; /* TTL of last received datagram */ node n; void *rbuf_alloc, *tbuf_alloc; - char *password; /* Password for MD5 authentication */ + char *password; /* Password for MD5 authentication */ + char *err; /* Error message */ } sock; sock *sock_new(pool *); /* Allocate new socket */ #define sk_new(X) sock_new(X) /* Wrapper to avoid name collision with OpenSSL */ int sk_open(sock *); /* Open socket */ +int sk_rx_ready(sock *s); int sk_send(sock *, unsigned len); /* Send data, <0=err, >0=ok, 0=sleep */ int sk_send_to(sock *, unsigned len, ip_addr to, unsigned port); /* sk_send to given destination */ void sk_reallocate(sock *); /* Free and allocate tbuf & rbuf */ @@ -61,39 +66,41 @@ void sk_set_rbsize(sock *s, uint val); /* Resize RX buffer */ void sk_set_tbsize(sock *s, uint val); /* Resize TX buffer, keeping content */ void sk_set_tbuf(sock *s, void *tbuf); /* Switch TX buffer, NULL-> return to internal */ void sk_dump_all(void); -int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */ -int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */ -/* Add or remove security associations for given passive socket */ -int sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd); -int sk_rx_ready(sock *s); +static inline int sk_send_buffer_empty(sock *sk) +{ return sk->tbuf == sk->tpos; } -/* Prepare UDP or IP socket to multicasting. s->iface and s->ttl must be set */ -int sk_setup_multicast(sock *s); -int sk_join_group(sock *s, ip_addr maddr); -int sk_leave_group(sock *s, ip_addr maddr); #ifdef IPV6 -int sk_set_ipv6_checksum(sock *s, int offset); -int sk_set_icmp_filter(sock *s, int p1, int p2); +#define sk_is_ipv4(X) 0 +#define sk_is_ipv6(X) 1 +#else +#define sk_is_ipv4(X) 1 +#define sk_is_ipv6(X) 0 #endif -int sk_set_broadcast(sock *s, int enable); -static inline int -sk_send_buffer_empty(sock *sk) -{ - return sk->tbuf == sk->tpos; -} +int sk_setup_multicast(sock *s); /* Prepare UDP or IP socket for multicasting */ +int sk_join_group(sock *s, ip_addr maddr); /* Join multicast group on sk iface */ +int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface */ +int sk_setup_broadcast(sock *s); +int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */ +int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */ +int sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd); +int sk_set_ipv6_checksum(sock *s, int offset); +int sk_set_icmp6_filter(sock *s, int p1, int p2); +void sk_log_error(sock *s, const char *p); + +extern int sk_priority_control; /* Suggested priority for control traffic, should be sysdep define */ -extern int sk_priority_control; /* Suggested priority for control traffic, should be sysdep define */ /* Socket flags */ -#define SKF_V6ONLY 1 /* Use IPV6_V6ONLY socket option */ -#define SKF_LADDR_RX 2 /* Report local address for RX packets */ -#define SKF_TTL_RX 4 /* Report TTL / Hop Limit for RX packets */ -#define SKF_BIND 8 /* Bind datagram socket to given source address */ +#define SKF_V4ONLY 0x01 /* Use IPv4 for IP sockets */ +#define SKF_V6ONLY 0x02 /* Use IPV6_V6ONLY socket option */ +#define SKF_LADDR_RX 0x04 /* Report local address for RX packets */ +#define SKF_TTL_RX 0x08 /* Report TTL / Hop Limit for RX packets */ +#define SKF_BIND 0x10 /* Bind datagram socket to given source address */ #define SKF_THREAD 0x100 /* Socked used in thread, Do not add to main loop */ #define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */ diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c index 964172d8..49b69bed 100644 --- a/proto/bfd/packets.c +++ b/proto/bfd/packets.c @@ -101,8 +101,8 @@ bfd_rx_hook(sock *sk, int len) uint err_val = 0; char fb[8]; - if ((sk->sport == BFD_CONTROL_PORT) && (sk->ttl < 255)) - DROP("wrong TTL", sk->ttl); + if ((sk->sport == BFD_CONTROL_PORT) && (sk->rcv_ttl < 255)) + DROP("wrong TTL", sk->rcv_ttl); if (len < BFD_BASE_LEN) DROP("too short", len); @@ -209,6 +209,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop) return sk; err: + sk_log_error(sk, p->p.name); rfree(sk); return NULL; } @@ -243,6 +244,7 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa) return sk; err: + sk_log_error(sk, p->p.name); rfree(sk); return NULL; } diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index ca619f31..b6239971 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -106,14 +106,11 @@ bgp_open(struct bgp_proto *p) struct config *cfg = p->cf->c.global; int errcode; - bgp_counter++; - if (!bgp_listen_sk) bgp_listen_sk = bgp_setup_listen_sk(cfg->listen_bgp_addr, cfg->listen_bgp_port, cfg->listen_bgp_flags); if (!bgp_listen_sk) { - bgp_counter--; errcode = BEM_NO_SOCKET; goto err; } @@ -121,16 +118,16 @@ bgp_open(struct bgp_proto *p) if (!bgp_linpool) bgp_linpool = lp_new(&root_pool, 4080); + bgp_counter++; + if (p->cf->password) - { - int rv = sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password); - if (rv < 0) - { - bgp_close(p, 0); - errcode = BEM_INVALID_MD5; - goto err; - } - } + if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, p->cf->password) < 0) + { + sk_log_error(bgp_listen_sk, p->p.name); + bgp_close(p, 0); + errcode = BEM_INVALID_MD5; + goto err; + } return 0; @@ -194,7 +191,8 @@ bgp_close(struct bgp_proto *p, int apply_md5) bgp_counter--; if (p->cf->password && apply_md5) - sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL); + if (sk_set_md5_auth(bgp_listen_sk, p->cf->remote_ip, p->cf->iface, NULL) < 0) + sk_log_error(bgp_listen_sk, p->p.name); if (!bgp_counter) { @@ -697,25 +695,21 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c bgp_conn_set_state(conn, BS_CONNECT); if (sk_open(s) < 0) - { - bgp_sock_err(s, 0); - return; - } + goto err; /* Set minimal receive TTL if needed */ if (p->cf->ttl_security) - { - DBG("Setting minimum received TTL to %d", 256 - hops); if (sk_set_min_ttl(s, 256 - hops) < 0) - { - log(L_ERR "TTL security configuration failed, closing session"); - bgp_sock_err(s, 0); - return; - } - } + goto err; DBG("BGP: Waiting for connect success\n"); bgp_start_timer(conn->connect_retry_timer, p->cf->connect_retry_time); + return; + + err: + sk_log_error(s, p->p.name); + bgp_sock_err(s, 0); + return; } /** @@ -760,32 +754,33 @@ bgp_incoming_connection(sock *sk, int dummy UNUSED) sk->dport, acc ? "accepted" : "rejected"); if (!acc) - goto err; + goto reject; int hops = p->cf->multihop ? : 1; + + if (sk_set_ttl(sk, p->cf->ttl_security ? 255 : hops) < 0) + goto err; + if (p->cf->ttl_security) - { - /* TTL security support */ - if ((sk_set_ttl(sk, 255) < 0) || - (sk_set_min_ttl(sk, 256 - hops) < 0)) - { - log(L_ERR "TTL security configuration failed, closing session"); + if (sk_set_min_ttl(sk, 256 - hops) < 0) goto err; - } - } - else - sk_set_ttl(sk, hops); bgp_setup_conn(p, &p->incoming_conn); bgp_setup_sk(&p->incoming_conn, sk); bgp_send_open(&p->incoming_conn); return 0; + + err: + sk_log_error(sk, p->p.name); + log(L_ERR "%s: Incoming connection aborted", p->p.name); + rfree(sk); + return 0; } } log(L_WARN "BGP: Unexpected connect from unknown address %I%J (port %d)", sk->daddr, ipa_has_link_scope(sk->daddr) ? sk->iface : NULL, sk->dport); - err: + reject: rfree(sk); return 0; } @@ -816,13 +811,15 @@ bgp_setup_listen_sk(ip_addr addr, unsigned port, u32 flags) s->err_hook = bgp_listen_sock_err; if (sk_open(s) < 0) - { - log(L_ERR "BGP: Unable to open listening socket"); - rfree(s); - return NULL; - } + goto err; return s; + + err: + sk_log_error(s, "BGP"); + log(L_ERR "BGP: Cannot open listening socket"); + rfree(s); + return NULL; } static void diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c index f4d9be55..50cf15e2 100644 --- a/proto/ospf/iface.c +++ b/proto/ospf/iface.c @@ -90,6 +90,8 @@ find_nbma_node_in(list *nnl, ip_addr ip) static int ospf_sk_open(struct ospf_iface *ifa) { + struct proto_ospf *po = ifa->oa->po; + sock *sk = sk_new(ifa->pool); sk->type = SK_IP; sk->dport = OSPF_PROTO; @@ -121,7 +123,7 @@ ospf_sk_open(struct ospf_iface *ifa) { ifa->all_routers = ifa->addr->brd; - if (sk_set_broadcast(sk, 1) < 0) + if (sk_setup_broadcast(sk) < 0) goto err; } else @@ -141,6 +143,7 @@ ospf_sk_open(struct ospf_iface *ifa) return 1; err: + sk_log_error(sk, po->proto.name); rfree(sk); return 0; } @@ -151,7 +154,9 @@ ospf_sk_join_dr(struct ospf_iface *ifa) if (ifa->sk_dr) return; - sk_join_group(ifa->sk, AllDRouters); + if (sk_join_group(ifa->sk, AllDRouters) < 0) + sk_log_error(ifa->sk, ifa->oa->po->proto.name); + ifa->sk_dr = 1; } @@ -161,15 +166,15 @@ ospf_sk_leave_dr(struct ospf_iface *ifa) if (!ifa->sk_dr) return; - sk_leave_group(ifa->sk, AllDRouters); + if (sk_leave_group(ifa->sk, AllDRouters) < 0) + sk_log_error(ifa->sk, ifa->oa->po->proto.name); + ifa->sk_dr = 0; } void ospf_open_vlink_sk(struct proto_ospf *po) { - struct proto *p = &po->proto; - sock *sk = sk_new(po->proto.pool); sk->type = SK_IP; sk->dport = OSPF_PROTO; @@ -197,8 +202,9 @@ ospf_open_vlink_sk(struct proto_ospf *po) return; err: + sk_log_error(sk, po->proto.name); + log(L_ERR "%s: Cannot open virtual link socket", po->proto.name); rfree(sk); - log(L_ERR "%s: Cannot open virtual link socket", p->name); } static void @@ -463,7 +469,7 @@ ospf_iface_add(struct object_lock *lock) /* Open socket if interface is not stub */ if (! ifa->stub && ! ospf_sk_open(ifa)) { - log(L_ERR "%s: Socket open failed on interface %s, declaring as stub", p->name, ifa->ifname); + log(L_ERR "%s: Cannot open socket for %s, declaring as stub", p->name, ifa->ifname); ifa->ioprob = OSPF_I_SK; ifa->stub = 1; } diff --git a/proto/ospf/packet.c b/proto/ospf/packet.c index cd4b8a97..1240b05c 100644 --- a/proto/ospf/packet.c +++ b/proto/ospf/packet.c @@ -308,9 +308,9 @@ ospf_rx_hook(sock *sk, int size) return 1; } - if (ifa->check_ttl && (sk->ttl < 255)) + if (ifa->check_ttl && (sk->rcv_ttl < 255)) { - log(L_ERR "%s%I - TTL %d (< 255)", mesg, sk->faddr, sk->ttl); + log(L_ERR "%s%I - TTL %d (< 255)", mesg, sk->faddr, sk->rcv_ttl); return 1; } diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 997fda3d..1d7e04f4 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -416,11 +416,11 @@ radv_sk_open(struct radv_iface *ifa) sk->data = ifa; sk->flags = SKF_LADDR_RX; - if (sk_open(sk) != 0) + if (sk_open(sk) < 0) goto err; /* We want listen just to ICMPv6 messages of type RS and RA */ - if (sk_set_icmp_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0) + if (sk_set_icmp6_filter(sk, ICMPV6_RS, ICMPV6_RA) < 0) goto err; if (sk_setup_multicast(sk) < 0) @@ -433,6 +433,7 @@ radv_sk_open(struct radv_iface *ifa) return 1; err: + sk_log_error(sk, ifa->ra->p.name); rfree(sk); return 0; } diff --git a/proto/rip/rip.c b/proto/rip/rip.c index 9730df77..bc9ffc5f 100644 --- a/proto/rip/rip.c +++ b/proto/rip/rip.c @@ -483,10 +483,10 @@ rip_rx(sock *s, int size) iface = i->iface; #endif - if (i->check_ttl && (s->ttl < 255)) + if (i->check_ttl && (s->rcv_ttl < 255)) { log( L_REMOTE "%s: Discarding packet with TTL %d (< 255) from %I on %s", - p->name, s->ttl, s->faddr, i->iface->name); + p->name, s->rcv_ttl, s->faddr, i->iface->name); return 1; } @@ -733,7 +733,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ log( L_WARN "%s: interface %s is too strange for me", p->name, rif->iface->name ); } else { - if (sk_open(rif->sock)<0) + if (sk_open(rif->sock) < 0) goto err; if (rif->multicast) @@ -745,7 +745,7 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ } else { - if (sk_set_broadcast(rif->sock, 1) < 0) + if (sk_setup_broadcast(rif->sock) < 0) goto err; } } @@ -755,7 +755,8 @@ new_iface(struct proto *p, struct iface *new, unsigned long flags, struct iface_ return rif; err: - log( L_ERR "%s: could not create socket for %s", p->name, rif->iface ? rif->iface->name : "(dummy)" ); + sk_log_error(rif->sock, p->name); + log(L_ERR "%s: Cannot open socket for %s", p->name, rif->iface ? rif->iface->name : "(dummy)" ); if (rif->iface) { rfree(rif->sock); mb_free(rif); diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c index aaeb7d90..26710375 100644 --- a/sysdep/bsd/krt-sock.c +++ b/sysdep/bsd/krt-sock.c @@ -251,9 +251,9 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e) _I0(gw) = 0xfe800000 | (i->index & 0x0000ffff); #endif - fill_in_sockaddr(&dst, net->n.prefix, NULL, 0); - fill_in_sockaddr(&mask, ipa_mkmask(net->n.pxlen), NULL, 0); - fill_in_sockaddr(&gate, gw, NULL, 0); + sockaddr_fill(&dst, BIRD_AF, net->n.prefix, NULL, 0); + sockaddr_fill(&mask, BIRD_AF, ipa_mkmask(net->n.pxlen), NULL, 0); + sockaddr_fill(&gate, BIRD_AF, gw, NULL, 0); switch (a->dest) { @@ -280,7 +280,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e) return -1; } - fill_in_sockaddr(&gate, i->addr->ip, NULL, 0); + sockaddr_fill(&dst, BIRD_AF, i->addr->ip, NULL, 0); msg.rtm.rtm_addrs |= RTA_GATEWAY; } break; @@ -366,20 +366,16 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan) GETADDR(&gate, RTA_GATEWAY); GETADDR(&mask, RTA_NETMASK); - if (sa_family_check(&dst)) - get_sockaddr(&dst, &idst, NULL, NULL, 0); - else + if (dst.sa.sa_family != BIRD_AF) SKIP("invalid DST"); - /* We will check later whether we have valid gateway addr */ - if (sa_family_check(&gate)) - get_sockaddr(&gate, &igate, NULL, NULL, 0); - else - igate = IPA_NONE; + idst = ipa_from_sa(&dst); + imask = ipa_from_sa(&mask); + igate = (gate.sa.sa_family == BIRD_AF) ? ipa_from_sa(&gate) : IPA_NONE; /* We do not test family for RTA_NETMASK, because BSD sends us some strange values, but interpreting them as IPv4/IPv6 works */ - get_sockaddr(&mask, &imask, NULL, NULL, 0); + int c = ipa_classify_net(idst); if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) @@ -648,12 +644,13 @@ krt_read_addr(struct ks_msg *msg, int scan) GETADDR (&brd, RTA_BRD); /* Some other family address */ - if (!sa_family_check(&addr)) + if (addr.sa.sa_family != BIRD_AF) return; - get_sockaddr(&addr, &iaddr, NULL, NULL, 0); - get_sockaddr(&mask, &imask, NULL, NULL, 0); - get_sockaddr(&brd, &ibrd, NULL, NULL, 0); + iaddr = ipa_from_sa(&addr); + imask = ipa_from_sa(&mask); + ibrd = ipa_from_sa(&brd); + if ((masklen = ipa_mklen(imask)) < 0) { @@ -806,7 +803,7 @@ krt_sysctl_scan(struct proto *p, int cmd, int table_id) mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; - mib[3] = BIRD_PF; + mib[3] = BIRD_AF; mib[4] = cmd; mib[5] = 0; mcnt = 6; diff --git a/sysdep/bsd/sysio.h b/sysdep/bsd/sysio.h index e45deb6f..fa3969bd 100644 --- a/sysdep/bsd/sysio.h +++ b/sysdep/bsd/sysio.h @@ -1,11 +1,16 @@ /* - * BIRD Internet Routing Daemon -- NetBSD Multicasting and Network Includes + * BIRD Internet Routing Daemon -- BSD Multicasting and Network Includes * * (c) 2004 Ondrej Filip * * Can be freely distributed and used under the terms of the GNU GPL. */ +#include +#include // Workaround for some BSDs +#include + + #ifdef __NetBSD__ #ifndef IP_RECVTTL @@ -22,173 +27,117 @@ #define TCP_MD5SIG TCP_SIGNATURE_ENABLE #endif -#ifdef IPV6 -static inline void -set_inaddr(struct in6_addr * ia, ip_addr a) +#define SA_LEN(x) (x).sa.sa_len + + +/* + * BSD IPv4 multicast syscalls + */ + +#define INIT_MREQ4(maddr,ifa) \ + { .imr_multiaddr = ipa_to_in4(maddr), .imr_interface = ipa_to_in4(ifa->addr->ip) } + +static inline int +sk_setup_multicast4(sock *s) { - ipa_hton(a); - memcpy(ia, &a, sizeof(a)); + struct in_addr ifa = ipa_to_in4(s->iface->addr->ip); + u8 ttl = s->ttl; + u8 n = 0; + + /* This defines where should we send _outgoing_ multicasts */ + if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_IF, &ifa, sizeof(ifa)) < 0) + ERR("IP_MULTICAST_IF"); + + if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + ERR("IP_MULTICAST_TTL"); + + if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &n, sizeof(n)) < 0) + ERR("IP_MULTICAST_LOOP"); + + return 0; +} + +static inline int +sk_join_group4(sock *s, ip_addr maddr) +{ + struct ip_mreq mr = INIT_MREQ4(maddr, s->iface); + + if (setsockopt(s->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) + ERR("IP_ADD_MEMBERSHIP"); + + return 0; +} + +static inline int +sk_leave_group4(sock *s, ip_addr maddr) +{ + struct ip_mreq mr = INIT_MREQ4(maddr, s->iface); + + if (setsockopt(s->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mr, sizeof(mr)) < 0) + ERR("IP_ADD_MEMBERSHIP"); + + return 0; +} + + +/* + * BSD IPv4 packet control messages + */ + +/* It uses IP_RECVDSTADDR / IP_RECVIF socket options instead of IP_PKTINFO */ + +#define CMSG4_SPACE_PKTINFO (CMSG_SPACE(sizeof(struct in_addr)) + \ + CMSG_SPACE(sizeof(struct sockaddr_dl))) +#define CMSG4_SPACE_TTL CMSG_SPACE(sizeof(char)) + +static inline int +sk_request_cmsg4_pktinfo(sock *s) +{ + int y = 1; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVDSTADDR, &y, sizeof(y)) < 0) + ERR("IP_RECVDSTADDR"); + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVIF, &y, sizeof(y)) < 0) + ERR("IP_RECVIF"); + + return 0; +} + +static inline int +sk_request_cmsg4_ttl(sock *s) +{ + int y = 1; + + if (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &y, sizeof(y)) < 0) + ERR("IP_RECVTTL"); + + return 0; } static inline void -get_inaddr(ip_addr *a, struct in6_addr *ia) +sk_process_cmsg4_pktinfo(sock *s, struct cmsghdr *cm) { - memcpy(a, ia, sizeof(*a)); - ipa_ntoh(*a); -} + if (cm->cmsg_type == IP_RECVDSTADDR) + s->laddr = ipa_from_in4(* (struct in_addr *) CMSG_DATA(cm)); - -#else - -#include -#include -#include // Workaround for some BSDs -#include - -static inline void -set_inaddr(struct in_addr * ia, ip_addr a) -{ - ipa_hton(a); - memcpy(&ia->s_addr, &a, sizeof(a)); + if (cm->cmsg_type == IP_RECVIF) + s->lifindex = ((struct sockaddr_dl *) CMSG_DATA(cm))->sdl_index; } static inline void -get_inaddr(ip_addr *a, struct in_addr *ia) +sk_process_cmsg4_ttl(sock *s, struct cmsghdr *cm) { - memcpy(a, &ia->s_addr, sizeof(*a)); - ipa_ntoh(*a); -} - - -/* BSD Multicast handling for IPv4 */ - -static inline char * -sysio_setup_multicast(sock *s) -{ - struct in_addr m; - u8 zero = 0; - u8 ttl = s->ttl; - - if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - return "IP_MULTICAST_LOOP"; - - if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) - return "IP_MULTICAST_TTL"; - - /* This defines where should we send _outgoing_ multicasts */ - set_inaddr(&m, s->iface->addr->ip); - if (setsockopt(s->fd, IPPROTO_IP, IP_MULTICAST_IF, &m, sizeof(m)) < 0) - return "IP_MULTICAST_IF"; - - return NULL; -} - - -static inline char * -sysio_join_group(sock *s, ip_addr maddr) -{ - struct ip_mreq mreq; - - bzero(&mreq, sizeof(mreq)); - set_inaddr(&mreq.imr_interface, s->iface->addr->ip); - set_inaddr(&mreq.imr_multiaddr, maddr); - - /* And this one sets interface for _receiving_ multicasts from */ - if (setsockopt(s->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return "IP_ADD_MEMBERSHIP"; - - return NULL; -} - -static inline char * -sysio_leave_group(sock *s, ip_addr maddr) -{ - struct ip_mreq mreq; - - bzero(&mreq, sizeof(mreq)); - set_inaddr(&mreq.imr_interface, s->iface->addr->ip); - set_inaddr(&mreq.imr_multiaddr, maddr); - - /* And this one sets interface for _receiving_ multicasts from */ - if (setsockopt(s->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) - return "IP_DROP_MEMBERSHIP"; - - 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)) + \ - CMSG_SPACE(sizeof(char))) -#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"; - } - - if ((s->flags & SKF_TTL_RX) && - (setsockopt(s->fd, IPPROTO_IP, IP_RECVTTL, &ok, sizeof(ok)) < 0)) - return "IP_RECVTTL"; - - - return NULL; + if (cm->cmsg_type == IP_RECVTTL) + s->rcv_ttl = * (unsigned char *) CMSG_DATA(cm); } static inline void -sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) +sk_prepare_cmsgs4(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) { - struct cmsghdr *cm; - struct in_addr *ra = NULL; - struct sockaddr_dl *ri = NULL; - unsigned char *ttl = NULL; + /* Unfortunately, IP_SENDSRCADDR does not work for raw IP sockets on BSD kernels */ - for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) - { - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVDSTADDR) - ra = (struct in_addr *) CMSG_DATA(cm); - - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVIF) - ri = (struct sockaddr_dl *) CMSG_DATA(cm); - - if (cm->cmsg_level == IPPROTO_IP && cm->cmsg_type == IP_RECVTTL) - ttl = (unsigned char *) CMSG_DATA(cm); - } - - if (s->flags & SKF_LADDR_RX) - { - s->laddr = IPA_NONE; - s->lifindex = 0; - - if (ra) - get_inaddr(&s->laddr, ra); - if (ri) - s->lifindex = ri->sdl_index; - } - - if (s->flags & SKF_TTL_RX) - s->ttl = ttl ? *ttl : -1; - - // log(L_WARN "RX %I %d", s->laddr, s->lifindex); -} - -/* Unfortunately, IP_SENDSRCADDR does not work for raw IP sockets on BSD kernels */ - -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; @@ -202,15 +151,14 @@ sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) cm->cmsg_len = CMSG_LEN(sizeof(*sa)); sa = (struct in_addr *) CMSG_DATA(cm); - set_inaddr(sa, s->saddr); + *sa = ipa_to_in4(s->saddr); msg->msg_controllen = cm->cmsg_len; #endif } - static void -fill_ip_header(sock *s, void *hdr, int dlen) +sk_prepare_ip_header(sock *s, void *hdr, int dlen) { struct ip *ip = hdr; @@ -222,8 +170,8 @@ fill_ip_header(sock *s, void *hdr, int dlen) 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); + ip->ip_src = ipa_to_in4(s->saddr); + ip->ip_dst = ipa_to_in4(s->daddr); #ifdef __OpenBSD__ /* OpenBSD expects ip_len in network order, other BSDs expect host order */ @@ -231,10 +179,11 @@ fill_ip_header(sock *s, void *hdr, int dlen) #endif } -#endif +/* + * Miscellaneous BSD socket syscalls + */ -#include #ifndef TCP_KEYLEN_MAX #define TCP_KEYLEN_MAX 80 #endif @@ -248,72 +197,69 @@ fill_ip_header(sock *s, void *hdr, int dlen) * management. */ -static int -sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) +int +sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) { int enable = 0; - if (passwd) - { - int len = strlen(passwd); - enable = len ? TCP_SIG_SPI : 0; + if (passwd && *passwd) + { + int len = strlen(passwd); + enable = TCP_SIG_SPI; - if (len > TCP_KEYLEN_MAX) - { - log(L_ERR "MD5 password too long"); - return -1; - } - } + if (len > TCP_KEYLEN_MAX) + ERR_MSG("MD5 password too long"); + } - int rv = setsockopt(s->fd, IPPROTO_TCP, TCP_MD5SIG, &enable, sizeof(enable)); - - 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; -} - - -#ifndef IPV6 - -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, IPPROTO_TCP, TCP_MD5SIG, &enable, sizeof(enable)) < 0) { if (errno == ENOPROTOOPT) - log(L_ERR "Kernel does not support IPv4 TTL security"); + ERR_MSG("Kernel does not support TCP MD5 signatures"); else - log(L_ERR "sk_set_min_ttl4: setsockopt: %m"); - - return -1; + ERR("TCP_MD5SIG"); } return 0; } -#else /* IPv6 */ - -static int -sk_set_min_ttl6(sock *s, int ttl) +static inline int +sk_set_min_ttl4(sock *s, int ttl) { - log(L_ERR "IPv6 TTL security not supported"); - return -1; + if (setsockopt(s->fd, IPPROTO_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0) + { + if (errno == ENOPROTOOPT) + ERR_MSG("Kernel does not support IPv4 TTL security"); + else + ERR("IP_MINTTL"); + } + + return 0; } -#endif +static inline int +sk_set_min_ttl6(sock *s, int ttl) +{ + ERR_MSG("Kernel does not support IPv6 TTL security"); +} +static inline int +sk_disable_mtu_disc4(sock *s) +{ + /* TODO: Set IP_DONTFRAG to 0 ? */ + return 0; +} + +static inline int +sk_disable_mtu_disc6(sock *s) +{ + /* TODO: Set IPV6_DONTFRAG to 0 ? */ + return 0; +} int sk_priority_control = -1; -static int +static inline int sk_set_priority(sock *s, int prio UNUSED) { - log(L_WARN "Socket priority not supported"); - return -1; + ERR_MSG("Socket priority not supported"); } 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 - -#ifdef IPV6 - -#ifndef IPV6_UNICAST_HOPS -/* Needed on glibc 2.0 systems */ -#include -#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 - -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); -} - -static inline char * -sysio_setup_multicast(sock *s) -{ - struct ip_mreqn m; - int zero = 0; - - 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"; - - /* 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"; - - return NULL; -} - -static inline char * -sysio_join_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_ADD_MEMBERSHIP, &m, sizeof(m)) < 0) - return "IP_ADD_MEMBERSHIP"; - - return NULL; -} - -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"; - - return NULL; -} - +#ifndef IP_MINTTL +#define IP_MINTTL 21 #endif +#ifndef IPV6_TCLASS +#define IPV6_TCLASS 67 +#endif + +#ifndef IPV6_MINHOPCOUNT +#define IPV6_MINHOPCOUNT 73 +#endif -/* For the case that we have older libc headers */ -/* Copied from Linux kernel file include/linux/tcp.h */ #ifndef TCP_MD5SIG #define TCP_MD5SIG 14 #define TCP_MD5SIG_MAXKEYLEN 80 -#include - 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) */ + u16 __tcpm_pad1; /* zero */ + u16 tcpm_keylen; /* key length */ + u32 __tcpm_pad2; /* zero */ + u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ }; #endif -static int -sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd) + +/* Linux does not care if sa_len is larger than needed */ +#define SA_LEN(x) sizeof(sockaddr) + + +/* + * Linux IPv4 multicast syscalls + */ + +#define INIT_MREQ4(maddr,ifa) \ + { .imr_multiaddr = ipa_to_in4(maddr), .imr_ifindex = ifa->index } + +static inline int +sk_setup_multicast4(sock *s) { - struct tcp_md5sig md5; + struct ip_mreqn mr = { .imr_ifindex = s->iface->index }; + int ttl = s->ttl; + int n = 0; - memset(&md5, 0, sizeof(md5)); - memcpy(&md5.tcpm_addr, (struct sockaddr *) sa, sizeof(*sa)); + /* 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"); - if (passwd) - { - int len = strlen(passwd); + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + ERR("IP_MULTICAST_TTL"); - if (len > TCP_MD5SIG_MAXKEYLEN) - { - log(L_ERR "MD5 password too long"); - return -1; - } + if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &n, sizeof(n)) < 0) + ERR("IP_MULTICAST_LOOP"); - md5.tcpm_keylen = len; - memcpy(&md5.tcpm_key, passwd, len); - } + return 0; +} - int rv = setsockopt(s->fd, SOL_TCP, TCP_MD5SIG, &md5, sizeof(md5)); +static inline int +sk_join_group4(sock *s, ip_addr maddr) +{ + struct ip_mreqn mr = INIT_MREQ4(maddr, s->iface); - 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"); - } + if (setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0) + ERR("IP_ADD_MEMBERSHIP"); - return rv; + return 0; +} + +static inline int +sk_leave_group4(sock *s, ip_addr maddr) +{ + struct ip_mreqn mr = INIT_MREQ4(maddr, s->iface); + + if (setsockopt(s->fd, SOL_IP, IP_DROP_MEMBERSHIP, &mr, sizeof(mr)) < 0) + ERR("IP_DROP_MEMBERSHIP"); + + 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); - } - - if (s->flags & SKF_LADDR_RX) - { - if (pi) - { - get_inaddr(&s->laddr, &pi->ipi_addr); - s->lifindex = pi->ipi_ifindex; - } - else - { - s->laddr = IPA_NONE; - s->lifindex = 0; - } - } - - if (s->flags & SKF_TTL_RX) - s->ttl = ttl ? *ttl : -1; - - return; + return 0; } -static void -sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) +static inline void +sk_process_cmsg4_pktinfo(sock *s, struct cmsghdr *cm) +{ + if (cm->cmsg_type == IP_PKTINFO) + { + struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cm); + s->laddr = ipa_from_in4(pi->ipi_addr); + s->lifindex = pi->ipi_ifindex; + } +} + +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 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"); -#ifndef IPV6 + md5.tcpm_keylen = len; + memcpy(&md5.tcpm_key, passwd, len); + } -static int + 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"); + } + + return 0; +} + +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; } + diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index 428f24cc..5a0c07e5 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -470,6 +471,7 @@ tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t) strcpy(x, ""); } + /** * DOC: Sockets * @@ -496,6 +498,473 @@ tm_format_datetime(char *x, struct timeformat *fmt_spec, bird_clock_t t) #endif +/* + * Sockaddr helper functions + */ + +static inline int sockaddr_length(int af) +{ return (af == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); } + +static inline void +sockaddr_fill4(struct sockaddr_in *sa, ip_addr a, struct iface *ifa, uint port) +{ + memset(sa, 0, sizeof(struct sockaddr_in)); +#ifdef HAVE_SIN_LEN + sa->sin_len = sizeof(struct sockaddr_in); +#endif + sa->sin_family = AF_INET; + sa->sin_port = htons(port); + sa->sin_addr = ipa_to_in4(a); +} + +static inline void +sockaddr_fill6(struct sockaddr_in6 *sa, ip_addr a, struct iface *ifa, uint port) +{ + memset(sa, 0, sizeof(struct sockaddr_in6)); +#ifdef SIN6_LEN + sa->sin6_len = sizeof(struct sockaddr_in6); +#endif + sa->sin6_family = AF_INET6; + sa->sin6_port = htons(port); + sa->sin6_flowinfo = 0; + sa->sin6_addr = ipa_to_in6(a); + + if (ifa && ipa_is_link_local(a)) + sa->sin6_scope_id = ifa->index; +} + +void +sockaddr_fill(sockaddr *sa, int af, ip_addr a, struct iface *ifa, uint port) +{ + if (af == AF_INET) + sockaddr_fill4((struct sockaddr_in *) sa, a, ifa, port); + else if (af == AF_INET6) + sockaddr_fill6((struct sockaddr_in6 *) sa, a, ifa, port); + else + bug("Unknown AF"); +} + +static inline void +sockaddr_read4(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, uint *port) +{ + *port = ntohs(sa->sin_port); + *a = ipa_from_in4(sa->sin_addr); +} + +static inline void +sockaddr_read6(struct sockaddr_in6 *sa, ip_addr *a, struct iface **ifa, uint *port) +{ + *port = ntohs(sa->sin6_port); + *a = ipa_from_in6(sa->sin6_addr); + + if (ifa && ipa_is_link_local(*a)) + *ifa = if_find_by_index(sa->sin6_scope_id); +} + +int +sockaddr_read(sockaddr *sa, int af, ip_addr *a, struct iface **ifa, uint *port) +{ + if (sa->sa.sa_family != af) + goto fail; + + if (af == AF_INET) + sockaddr_read4((struct sockaddr_in *) sa, a, ifa, port); + else if (af == AF_INET6) + sockaddr_read6((struct sockaddr_in6 *) sa, a, ifa, port); + else + goto fail; + + return 0; + + fail: + *a = IPA_NONE; + *port = 0; + return -1; +} + + +/* + * IPv6 multicast syscalls + */ + +/* Fortunately standardized in RFC 3493 */ + +#define INIT_MREQ6(maddr,ifa) \ + { .ipv6mr_multiaddr = ipa_to_in6(maddr), .ipv6mr_interface = ifa->index } + +static inline int +sk_setup_multicast6(sock *s) +{ + int index = s->iface->index; + int ttl = s->ttl; + int n = 0; + + if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0) + ERR("IPV6_MULTICAST_IF"); + + if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) + ERR("IPV6_MULTICAST_HOPS"); + + if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &n, sizeof(n)) < 0) + ERR("IPV6_MULTICAST_LOOP"); + + return 0; +} + +static inline int +sk_join_group6(sock *s, ip_addr maddr) +{ + struct ipv6_mreq mr = INIT_MREQ6(maddr, s->iface); + + if (setsockopt(s->fd, SOL_IPV6, IPV6_JOIN_GROUP, &mr, sizeof(mr)) < 0) + ERR("IPV6_JOIN_GROUP"); + + return 0; +} + +static inline int +sk_leave_group6(sock *s, ip_addr maddr) +{ + struct ipv6_mreq mr = INIT_MREQ6(maddr, s->iface); + + if (setsockopt(s->fd, SOL_IPV6, IPV6_LEAVE_GROUP, &mr, sizeof(mr)) < 0) + ERR("IPV6_LEAVE_GROUP"); + + return 0; +} + + +/* + * IPv6 packet control messages + */ + +/* Also standardized, in RFC 3542 */ + +/* + * RFC 2292 uses IPV6_PKTINFO for both the socket option and the cmsg + * type, RFC 3542 changed the socket option to IPV6_RECVPKTINFO. If we + * don't have IPV6_RECVPKTINFO we suppose the OS implements the older + * RFC and we use IPV6_PKTINFO. + */ +#ifndef IPV6_RECVPKTINFO +#define IPV6_RECVPKTINFO IPV6_PKTINFO +#endif +/* + * Same goes for IPV6_HOPLIMIT -> IPV6_RECVHOPLIMIT. + */ +#ifndef IPV6_RECVHOPLIMIT +#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT +#endif + + +#define CMSG6_SPACE_PKTINFO CMSG_SPACE(sizeof(struct in6_pktinfo)) +#define CMSG6_SPACE_TTL CMSG_SPACE(sizeof(int)) + +static inline int +sk_request_cmsg6_pktinfo(sock *s) +{ + int y = 1; + + if (setsockopt(s->fd, SOL_IPV6, IPV6_RECVPKTINFO, &y, sizeof(y)) < 0) + ERR("IPV6_RECVPKTINFO"); + + return 0; +} + +static inline int +sk_request_cmsg6_ttl(sock *s) +{ + int y = 1; + + if (setsockopt(s->fd, SOL_IPV6, IPV6_RECVHOPLIMIT, &y, sizeof(y)) < 0) + ERR("IPV6_RECVHOPLIMIT"); + + return 0; +} + +static inline void +sk_process_cmsg6_pktinfo(sock *s, struct cmsghdr *cm) +{ + if (cm->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *pi = (struct in6_pktinfo *) CMSG_DATA(cm); + s->laddr = ipa_from_in6(pi->ipi6_addr); + s->lifindex = pi->ipi6_ifindex; + } +} + +static inline void +sk_process_cmsg6_ttl(sock *s, struct cmsghdr *cm) +{ + if (cm->cmsg_type == IPV6_HOPLIMIT) + s->rcv_ttl = * (int *) CMSG_DATA(cm); +} + +static inline void +sk_prepare_cmsgs6(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) +{ + struct cmsghdr *cm; + struct in6_pktinfo *pi; + + msg->msg_control = cbuf; + msg->msg_controllen = cbuflen; + + cm = CMSG_FIRSTHDR(msg); + cm->cmsg_level = SOL_IPV6; + cm->cmsg_type = IPV6_PKTINFO; + cm->cmsg_len = CMSG_LEN(sizeof(*pi)); + + pi = (struct in6_pktinfo *) CMSG_DATA(cm); + pi->ipi6_ifindex = s->iface ? s->iface->index : 0; + pi->ipi6_addr = ipa_to_in6(s->saddr); + + msg->msg_controllen = cm->cmsg_len; +} + + +/* + * Miscellaneous socket syscalls + */ + +static inline int +sk_set_ttl4(sock *s, int ttl) +{ + if (setsockopt(s->fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) + ERR("IP_TTL"); + + return 0; +} + +static inline int +sk_set_ttl6(sock *s, int ttl) +{ + if (setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) + ERR("IPV6_UNICAST_HOPS"); + + return 0; +} + +static inline int +sk_set_tos4(sock *s, int tos) +{ + if (setsockopt(s->fd, SOL_IP, IP_TOS, &tos, sizeof(tos)) < 0) + ERR("IP_TOS"); + + return 0; +} + +static inline int +sk_set_tos6(sock *s, int tos) +{ + if (setsockopt(s->fd, SOL_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) < 0) + ERR("IPV6_TCLASS"); + + return 0; +} + + +/* + * Public socket functions + */ + +/** + * sk_setup_multicast - enable multicast for given socket + * @s: socket + * + * Prepare transmission of multicast packets for given datagram socket. + * The socket must have defined @iface. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_setup_multicast(sock *s) +{ + ASSERT(s->iface); + + if (sk_is_ipv4(s)) + return sk_setup_multicast4(s); + else + return sk_setup_multicast6(s); +} + +/** + * sk_join_group - join multicast group for given socket + * @s: socket + * @maddr: multicast address + * + * Join multicast group for given datagram socket and associated interface. + * The socket must have defined @iface. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_join_group(sock *s, ip_addr maddr) +{ + if (sk_is_ipv4(s)) + return sk_join_group4(s, maddr); + else + return sk_join_group6(s, maddr); +} + +/** + * sk_leave_group - leave multicast group for given socket + * @s: socket + * @maddr: multicast address + * + * Leave multicast group for given datagram socket and associated interface. + * The socket must have defined @iface. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_leave_group(sock *s, ip_addr maddr) +{ + if (sk_is_ipv4(s)) + return sk_leave_group4(s, maddr); + else + return sk_leave_group6(s, maddr); +} + +/** + * sk_setup_broadcast - enable broadcast for given socket + * @s: socket + * + * Allow reception and transmission of broadcast packets for given datagram + * socket. The socket must have defined @iface. For transmission, packets should + * be send to @brd address of @iface. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_setup_broadcast(sock *s) +{ + int y = 1; + + if (setsockopt(s->fd, SOL_SOCKET, SO_BROADCAST, &y, sizeof(y)) < 0) + ERR("SO_BROADCAST"); + + return 0; +} + +/** + * sk_set_ttl - set transmit TTL for given socket + * @s: socket + * @ttl: TTL value + * + * Set TTL for already opened connections when TTL was not set before. Useful + * for accepted connections when different ones should have different TTL. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_set_ttl(sock *s, int ttl) +{ + s->ttl = ttl; + + if (sk_is_ipv4(s)) + return sk_set_ttl4(s, ttl); + else + return sk_set_ttl6(s, ttl); +} + +/** + * sk_set_min_ttl - set minimal accepted TTL for given socket + * @s: socket + * @ttl: TTL value + * + * Set minimal accepted TTL for given socket. Can be used for TTL security. + * implementations. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_set_min_ttl(sock *s, int ttl) +{ + if (sk_is_ipv4(s)) + return sk_set_min_ttl4(s, ttl); + else + return sk_set_min_ttl6(s, ttl); +} + +#if 0 +/** + * sk_set_md5_auth - add / remove MD5 security association for given socket + * @s: socket + * @a: IP address of the other side + * @ifa: Interface for link-local IP address + * @passwd: password used for MD5 authentication + * + * In TCP MD5 handling code in kernel, there is a set of pairs (address, + * password) used to choose password according to address of the other side. + * This function is useful for listening socket, for active sockets it is enough + * to set s->password field. + * + * When called with passwd != NULL, the new pair is added, + * When called with passwd == NULL, the existing pair is removed. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) +{ DUMMY; } +#endif + +/** + * sk_set_ipv6_checksum - specify IPv6 checksum offset for given socket + * @s: socket + * @offset: offset + * + * Specify IPv6 checksum field offset for given raw IPv6 socket. After that, the + * kernel will automatically fill it for outgoing packets and check it for + * incoming packets. Should not be used on ICMPv6 sockets, where the position is + * known to the kernel. + * + * Result: 0 for success, -1 for an error. + */ + +int +sk_set_ipv6_checksum(sock *s, int offset) +{ + if (setsockopt(s->fd, SOL_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)) < 0) + ERR("IPV6_CHECKSUM"); + + return 0; +} + +int +sk_set_icmp6_filter(sock *s, int p1, int p2) +{ + /* a bit of lame interface, but it is here only for Radv */ + struct icmp6_filter f; + + ICMP6_FILTER_SETBLOCKALL(&f); + ICMP6_FILTER_SETPASS(p1, &f); + ICMP6_FILTER_SETPASS(p2, &f); + + if (setsockopt(s->fd, SOL_ICMPV6, ICMP6_FILTER, &f, sizeof(f)) < 0) + ERR("ICMP6_FILTER"); + + return 0; +} + +void +sk_log_error(sock *s, const char *p) +{ + log(L_ERR "%s: Socket error: %s%#m", p, s->err); +} + + +/* + * Actual struct birdsock code + */ + static list sock_list; static struct birdsock *current_sock; static struct birdsock *stored_sock; @@ -525,15 +994,15 @@ static void sk_free_bufs(sock *s) { if (s->rbuf_alloc) - { - xfree(s->rbuf_alloc); - s->rbuf = s->rbuf_alloc = NULL; - } + { + xfree(s->rbuf_alloc); + s->rbuf = s->rbuf_alloc = NULL; + } if (s->tbuf_alloc) - { - xfree(s->tbuf_alloc); - s->tbuf = s->tbuf_alloc = NULL; - } + { + xfree(s->tbuf_alloc); + s->tbuf = s->tbuf_alloc = NULL; + } } static void @@ -543,20 +1012,20 @@ sk_free(resource *r) sk_free_bufs(s); if (s->fd >= 0) - { - close(s->fd); + { + close(s->fd); - /* FIXME: we should call sk_stop() for SKF_THREAD sockets */ - if (s->flags & SKF_THREAD) - return; + /* FIXME: we should call sk_stop() for SKF_THREAD sockets */ + if (s->flags & SKF_THREAD) + return; - if (s == current_sock) - current_sock = sk_next(s); - if (s == stored_sock) - stored_sock = sk_next(s); - rem_node(&s->n); - sock_recalc_fdsets_p = 1; - } + if (s == current_sock) + current_sock = sk_next(s); + if (s == stored_sock) + stored_sock = sk_next(s); + rem_node(&s->n); + sock_recalc_fdsets_p = 1; + } } void @@ -607,7 +1076,7 @@ static void sk_dump(resource *r) { sock *s = (sock *) r; - static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", "UDP/MC", "IP", "IP/MC", "MAGIC", "UNIX<", "UNIX", "DEL!" }; + static char *sk_type_names[] = { "TCP<", "TCP>", "TCP", "UDP", NULL, "IP", NULL, "MAGIC", "UNIX<", "UNIX", "DEL!" }; debug("(%s, ud=%p, sa=%08x, sp=%d, da=%08x, dp=%d, tos=%d, ttl=%d, if=%s)\n", sk_type_names[s->type], @@ -652,207 +1121,27 @@ sock_new(pool *p) return s; } -static void -sk_insert(sock *s) -{ - add_tail(&sock_list, &s->n); - sock_recalc_fdsets_p = 1; -} - -#ifdef IPV6 - -void -fill_in_sockaddr(struct sockaddr_in6 *sa, ip_addr a, struct iface *ifa, unsigned port) -{ - memset(sa, 0, sizeof (struct sockaddr_in6)); - sa->sin6_family = AF_INET6; - sa->sin6_port = htons(port); - sa->sin6_flowinfo = 0; -#ifdef HAVE_SIN_LEN - sa->sin6_len = sizeof(struct sockaddr_in6); -#endif - set_inaddr(&sa->sin6_addr, a); - - if (ifa && ipa_has_link_scope(a)) - sa->sin6_scope_id = ifa->index; -} - -void -get_sockaddr(struct sockaddr_in6 *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check) -{ - if (check && sa->sin6_family != AF_INET6) - bug("get_sockaddr called for wrong address family (%d)", sa->sin6_family); - if (port) - *port = ntohs(sa->sin6_port); - memcpy(a, &sa->sin6_addr, sizeof(*a)); - ipa_ntoh(*a); - - if (ifa && ipa_has_link_scope(*a)) - *ifa = if_find_by_index(sa->sin6_scope_id); -} - -#else - -void -fill_in_sockaddr(struct sockaddr_in *sa, ip_addr a, struct iface *ifa, unsigned port) -{ - memset (sa, 0, sizeof (struct sockaddr_in)); - sa->sin_family = AF_INET; - sa->sin_port = htons(port); -#ifdef HAVE_SIN_LEN - sa->sin_len = sizeof(struct sockaddr_in); -#endif - set_inaddr(&sa->sin_addr, a); -} - -void -get_sockaddr(struct sockaddr_in *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check) -{ - if (check && sa->sin_family != AF_INET) - bug("get_sockaddr called for wrong address family (%d)", sa->sin_family); - if (port) - *port = ntohs(sa->sin_port); - memcpy(a, &sa->sin_addr.s_addr, sizeof(*a)); - ipa_ntoh(*a); -} - -#endif - - -#ifdef IPV6 - -/* PKTINFO handling is also standardized in IPv6 */ -#define CMSG_RX_SPACE (CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))) -#define CMSG_TX_SPACE CMSG_SPACE(sizeof(struct in6_pktinfo)) - -/* - * RFC 2292 uses IPV6_PKTINFO for both the socket option and the cmsg - * type, RFC 3542 changed the socket option to IPV6_RECVPKTINFO. If we - * don't have IPV6_RECVPKTINFO we suppose the OS implements the older - * RFC and we use IPV6_PKTINFO. - */ -#ifndef IPV6_RECVPKTINFO -#define IPV6_RECVPKTINFO IPV6_PKTINFO -#endif -/* - * Same goes for IPV6_HOPLIMIT -> IPV6_RECVHOPLIMIT. - */ -#ifndef IPV6_RECVHOPLIMIT -#define IPV6_RECVHOPLIMIT IPV6_HOPLIMIT -#endif - -static char * -sysio_register_cmsgs(sock *s) -{ - int ok = 1; - - if ((s->flags & SKF_LADDR_RX) && - (setsockopt(s->fd, SOL_IPV6, IPV6_RECVPKTINFO, &ok, sizeof(ok)) < 0)) - return "IPV6_RECVPKTINFO"; - - if ((s->flags & SKF_TTL_RX) && - (setsockopt(s->fd, SOL_IPV6, IPV6_RECVHOPLIMIT, &ok, sizeof(ok)) < 0)) - return "IPV6_RECVHOPLIMIT"; - - return NULL; -} - -static void -sysio_process_rx_cmsgs(sock *s, struct msghdr *msg) -{ - struct cmsghdr *cm; - struct in6_pktinfo *pi = NULL; - int *hlim = NULL; - - for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) - { - if (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_PKTINFO) - pi = (struct in6_pktinfo *) CMSG_DATA(cm); - - if (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_HOPLIMIT) - hlim = (int *) CMSG_DATA(cm); - } - - if (s->flags & SKF_LADDR_RX) - { - if (pi) - { - get_inaddr(&s->laddr, &pi->ipi6_addr); - s->lifindex = pi->ipi6_ifindex; - } - else - { - s->laddr = IPA_NONE; - s->lifindex = 0; - } - } - - if (s->flags & SKF_TTL_RX) - s->ttl = hlim ? *hlim : -1; - - return; -} - -static void -sysio_prepare_tx_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) -{ - struct cmsghdr *cm; - struct in6_pktinfo *pi; - - msg->msg_control = cbuf; - msg->msg_controllen = cbuflen; - - cm = CMSG_FIRSTHDR(msg); - cm->cmsg_level = SOL_IPV6; - cm->cmsg_type = IPV6_PKTINFO; - cm->cmsg_len = CMSG_LEN(sizeof(*pi)); - - pi = (struct in6_pktinfo *) CMSG_DATA(cm); - pi->ipi6_ifindex = s->iface ? s->iface->index : 0; - set_inaddr(&pi->ipi6_addr, s->saddr); - - msg->msg_controllen = cm->cmsg_len; -} - -#endif - -static char * -sk_set_ttl_int(sock *s) -{ -#ifdef IPV6 - if (setsockopt(s->fd, SOL_IPV6, IPV6_UNICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) - return "IPV6_UNICAST_HOPS"; -#else - if (setsockopt(s->fd, SOL_IP, IP_TTL, &s->ttl, sizeof(s->ttl)) < 0) - return "IP_TTL"; -#endif - return NULL; -} - -#define ERR(x) do { err = x; goto bad; } while(0) -#define WARN(x) log(L_WARN "sk_setup: %s: %m", x) - -static char * +static int sk_setup(sock *s) { - int one = 1; + int y = 1; int fd = s->fd; - char *err = NULL; if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) - ERR("fcntl(O_NONBLOCK)"); - if (s->type == SK_UNIX) - return NULL; + ERR("O_NONBLOCK"); + + if (!s->af) + return 0; 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)) + if (sk_is_ipv4(s) && (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) + if (setsockopt(fd, SOL_IP, IP_HDRINCL, &y, sizeof(y)) < 0) ERR("IP_HDRINCL"); } #endif @@ -867,276 +1156,84 @@ sk_setup(sock *s) #endif #ifdef CONFIG_UNIX_DONTROUTE - if (setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &one, sizeof(one)) < 0) + if (setsockopt(s->fd, SOL_SOCKET, SO_DONTROUTE, &y, sizeof(y)) < 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"); -#else - if ((s->tos >= 0) && setsockopt(fd, SOL_IP, IP_TOS, &s->tos, sizeof(s->tos)) < 0) - WARN("IP_TOS"); -#endif - if (s->priority >= 0) - sk_set_priority(s, s->priority); - -#ifdef IPV6 - if ((s->flags & SKF_V6ONLY) && setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &one, sizeof(one)) < 0) - WARN("IPV6_V6ONLY"); -#endif - -bad: - return err; -} - -/** - * sk_set_ttl - set transmit TTL for given socket. - * @s: socket - * @ttl: TTL value - * - * Set TTL for already opened connections when TTL was not set before. - * Useful for accepted connections when different ones should have - * different TTL. - * - * Result: 0 for success, -1 for an error. - */ - -int -sk_set_ttl(sock *s, int ttl) -{ - char *err; - - s->ttl = ttl; - if (err = sk_set_ttl_int(s)) - log(L_ERR "sk_set_ttl: %s: %m", err); - - return (err ? -1 : 0); -} - -/** - * sk_set_min_ttl - set minimal accepted TTL for given socket. - * @s: socket - * @ttl: TTL value - * - * Can be used in TTL security implementation - * - * Result: 0 for success, -1 for an error. - */ - -int -sk_set_min_ttl(sock *s, int ttl) -{ - int err; -#ifdef IPV6 - err = sk_set_min_ttl6(s, ttl); -#else - err = sk_set_min_ttl4(s, ttl); -#endif - - return err; -} - -/** - * sk_set_md5_auth - add / remove MD5 security association for given socket. - * @s: socket - * @a: IP address of the other side - * @ifa: Interface for link-local IP address - * @passwd: password used for MD5 authentication - * - * In TCP MD5 handling code in kernel, there is a set of pairs - * (address, password) used to choose password according to - * address of the other side. This function is useful for - * listening socket, for active sockets it is enough to set - * s->password field. - * - * When called with passwd != NULL, the new pair is added, - * When called with passwd == NULL, the existing pair is removed. - * - * Result: 0 for success, -1 for an error. - */ - -int -sk_set_md5_auth(sock *s, ip_addr a, struct iface *ifa, char *passwd) -{ - sockaddr sa; - fill_in_sockaddr(&sa, a, ifa, 0); - return sk_set_md5_auth_int(s, &sa, passwd); -} - -int -sk_set_broadcast(sock *s, int enable) -{ - if (setsockopt(s->fd, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)) < 0) - { - log(L_ERR "sk_set_broadcast: SO_BROADCAST: %m"); + if (sk_set_priority(s, s->priority) < 0) return -1; - } + + if (sk_is_ipv4(s)) + { + if (s->flags & SKF_LADDR_RX) + if (sk_request_cmsg4_pktinfo(s) < 0) + return -1; + + if (s->flags & SKF_TTL_RX) + if (sk_request_cmsg4_ttl(s) < 0) + return -1; + + if ((s->type == SK_UDP) || (s->type == SK_IP)) + if (sk_disable_mtu_disc4(s) < 0) + return -1; + + if (s->ttl >= 0) + if (sk_set_ttl4(s, s->ttl) < 0) + return -1; + + if (s->tos >= 0) + if (sk_set_tos4(s, s->tos) < 0) + return -1; + } + + if (sk_is_ipv6(s)) + { + if (s->flags & SKF_V6ONLY) + if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &y, sizeof(y)) < 0) + ERR("IPV6_V6ONLY"); + + if (s->flags & SKF_LADDR_RX) + if (sk_request_cmsg6_pktinfo(s) < 0) + return -1; + + if (s->flags & SKF_TTL_RX) + if (sk_request_cmsg6_ttl(s) < 0) + return -1; + + if ((s->type == SK_UDP) || (s->type == SK_IP)) + if (sk_disable_mtu_disc6(s) < 0) + return -1; + + if (s->ttl >= 0) + if (sk_set_ttl6(s, s->ttl) < 0) + return -1; + + if (s->tos >= 0) + if (sk_set_tos6(s, s->tos) < 0) + return -1; + } return 0; } - -#ifdef IPV6 - -int -sk_set_ipv6_checksum(sock *s, int offset) +static void +sk_insert(sock *s) { - if (setsockopt(s->fd, SOL_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset)) < 0) - { - log(L_ERR "sk_set_ipv6_checksum: IPV6_CHECKSUM: %m"); - return -1; - } - - return 0; + add_tail(&sock_list, &s->n); + sock_recalc_fdsets_p = 1; } -int -sk_set_icmp_filter(sock *s, int p1, int p2) -{ - /* a bit of lame interface, but it is here only for Radv */ - struct icmp6_filter f; - - ICMP6_FILTER_SETBLOCKALL(&f); - ICMP6_FILTER_SETPASS(p1, &f); - ICMP6_FILTER_SETPASS(p2, &f); - - if (setsockopt(s->fd, SOL_ICMPV6, ICMP6_FILTER, &f, sizeof(f)) < 0) - { - log(L_ERR "sk_setup_icmp_filter: ICMP6_FILTER: %m"); - return -1; - } - - return 0; -} - -int -sk_setup_multicast(sock *s) -{ - char *err; - int zero = 0; - int index; - - ASSERT(s->iface); - - index = s->iface->index; - if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &s->ttl, sizeof(s->ttl)) < 0) - ERR("IPV6_MULTICAST_HOPS"); - if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)) < 0) - ERR("IPV6_MULTICAST_LOOP"); - if (setsockopt(s->fd, SOL_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0) - ERR("IPV6_MULTICAST_IF"); - - return 0; - -bad: - log(L_ERR "sk_setup_multicast: %s: %m", err); - 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); - mreq.ipv6mr_interface = s->iface->index; - - if (setsockopt(s->fd, SOL_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) - { - log(L_ERR "sk_join_group: IPV6_JOIN_GROUP: %m"); - return -1; - } - - return 0; -} - -int -sk_leave_group(sock *s, ip_addr maddr) -{ - struct ipv6_mreq mreq; - - set_inaddr(&mreq.ipv6mr_multiaddr, maddr); - mreq.ipv6mr_interface = s->iface->index; - - if (setsockopt(s->fd, SOL_IPV6, IPV6_LEAVE_GROUP, &mreq, sizeof(mreq)) < 0) - { - log(L_ERR "sk_leave_group: IPV6_LEAVE_GROUP: %m"); - return -1; - } - - return 0; -} - -#else /* IPV4 */ - -int -sk_setup_multicast(sock *s) -{ - char *err; - - ASSERT(s->iface); - - if (err = sysio_setup_multicast(s)) - { - log(L_ERR "sk_setup_multicast: %s: %m", err); - return -1; - } - - return 0; -} - -int -sk_join_group(sock *s, ip_addr maddr) -{ - char *err; - - if (err = sysio_join_group(s, maddr)) - { - log(L_ERR "sk_join_group: %s: %m", err); - return -1; - } - - return 0; -} - -int -sk_leave_group(sock *s, ip_addr maddr) -{ - char *err; - - if (err = sysio_leave_group(s, maddr)) - { - log(L_ERR "sk_leave_group: %s: %m", err); - return -1; - } - - return 0; -} - -#endif - - static void sk_tcp_connected(sock *s) { - sockaddr lsa; - int lsa_len = sizeof(lsa); - if (getsockname(s->fd, (struct sockaddr *) &lsa, &lsa_len) == 0) - get_sockaddr(&lsa, &s->saddr, &s->iface, &s->sport, 1); + sockaddr sa; + int sa_len = sizeof(sa); + + if ((getsockname(s->fd, &sa.sa, &sa_len) < 0) || + (sockaddr_read(&sa, s->af, &s->saddr, &s->iface, &s->sport) < 0)) + log(L_WARN "SOCK: Cannot get local IP address for TCP>"); s->type = SK_TCP; sk_alloc_bufs(s); @@ -1144,44 +1241,55 @@ sk_tcp_connected(sock *s) } static int -sk_passive_connected(sock *s, struct sockaddr *sa, int al, int type) +sk_passive_connected(sock *s, int type) { - int fd = accept(s->fd, sa, &al); - if (fd >= 0) - { - sock *t = sk_new(s->pool); - char *err; - t->type = type; - t->fd = fd; - t->ttl = s->ttl; - t->tos = s->tos; - t->rbsize = s->rbsize; - t->tbsize = s->tbsize; - if (type == SK_TCP) - { - sockaddr lsa; - int lsa_len = sizeof(lsa); - if (getsockname(fd, (struct sockaddr *) &lsa, &lsa_len) == 0) - get_sockaddr(&lsa, &t->saddr, &t->iface, &t->sport, 1); + sockaddr loc_sa, rem_sa; + int loc_sa_len = sizeof(loc_sa); + int rem_sa_len = sizeof(rem_sa); - get_sockaddr((sockaddr *) sa, &t->daddr, &t->iface, &t->dport, 1); - } - sk_insert(t); - if (err = sk_setup(t)) - { - log(L_ERR "Incoming connection: %s: %m", err); - rfree(t); - return 1; - } - sk_alloc_bufs(t); - s->rx_hook(t, 0); - return 1; - } - else if (errno != EINTR && errno != EAGAIN) - { + int fd = accept(s->fd, ((type == SK_TCP) ? &rem_sa.sa : NULL), &rem_sa_len); + if (fd < 0) + { + if ((errno != EINTR) && (errno != EAGAIN)) s->err_hook(s, errno); - } - return 0; + return 0; + } + + sock *t = sk_new(s->pool); + t->type = type; + t->fd = fd; + t->af = s->af; + t->ttl = s->ttl; + t->tos = s->tos; + t->rbsize = s->rbsize; + t->tbsize = s->tbsize; + + if (type == SK_TCP) + { + if ((getsockname(fd, &loc_sa.sa, &loc_sa_len) < 0) || + (sockaddr_read(&loc_sa, s->af, &t->saddr, &t->iface, &t->sport) < 0)) + log(L_WARN "SOCK: Cannot get local IP address for TCP<"); + + if (sockaddr_read(&rem_sa, s->af, &t->daddr, &t->iface, &t->dport) < 0) + log(L_WARN "SOCK: Cannot get remote IP address for TCP<"); + } + + if (sk_setup(t) < 0) + { + /* FIXME: Call err_hook instead ? */ + log(L_ERR "SOCK: Incoming connection: %s%#m", t->err); + + /* FIXME: handle it better in rfree() */ + close(t->fd); + t->fd = -1; + rfree(t); + return 1; + } + + sk_insert(t); + sk_alloc_bufs(t); + s->rx_hook(t, 0); + return 1; } /** @@ -1197,161 +1305,185 @@ sk_passive_connected(sock *s, struct sockaddr *sa, int al, int type) int sk_open(sock *s) { - int fd; - int one = 1; + int af = BIRD_AF; + int fd = -1; int do_bind = 0; int bind_port = 0; ip_addr bind_addr = IPA_NONE; sockaddr sa; - char *err; 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_TCP_ACTIVE: + s->ttx = ""; /* Force s->ttx != s->tpos */ + /* Fall thru */ + case SK_TCP_PASSIVE: + fd = socket(af, 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(af, SOCK_DGRAM, IPPROTO_UDP); + bind_port = s->sport; + bind_addr = (s->flags & SKF_BIND) ? s->saddr : IPA_NONE; + do_bind = 1; + 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(af, 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_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: + af = 0; + fd = s->fd; + break; - case SK_MAGIC: - fd = s->fd; - break; + default: + bug("sk_open() called for invalid sock type %d", s->type); + } - default: - bug("sk_open() called for invalid sock type %d", s->type); - } if (fd < 0) - die("sk_open: socket: %m"); + ERR("socket"); + + s->af = af; s->fd = fd; - if (err = sk_setup(s)) - goto bad; + if (sk_setup(s) < 0) + goto err; if (do_bind) + { + if (bind_port) { - if (bind_port) - { - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) - ERR("SO_REUSEADDR"); + int y = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &y, sizeof(y)) < 0) + ERR2("SO_REUSEADDR"); #ifdef CONFIG_NO_IFACE_BIND - /* Workaround missing ability to bind to an iface */ - 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"); - } + /* Workaround missing ability to bind to an iface */ + if ((s->type == SK_UDP) && s->iface && ipa_zero(bind_addr)) + { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &y, sizeof(y)) < 0) + ERR2("SO_REUSEPORT"); + } #endif - } - - 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); + sockaddr_fill(&sa, af, bind_addr, s->iface, bind_port); + if (bind(fd, &sa.sa, SA_LEN(sa)) < 0) + ERR2("bind"); + } if (s->password) - { - int rv = sk_set_md5_auth_int(s, &sa, s->password); - if (rv < 0) - goto bad_no_log; - } + if (sk_set_md5_auth(s, s->daddr, s->iface, s->password) < 0) + goto err; switch (s->type) - { - case SK_TCP_ACTIVE: - if (connect(fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0) - sk_tcp_connected(s); - else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && - errno != ECONNREFUSED && errno != EHOSTUNREACH && errno != ENETUNREACH) - ERR("connect"); - break; - case SK_TCP_PASSIVE: - if (listen(fd, 8)) - ERR("listen"); - break; - case SK_MAGIC: - break; - default: - sk_alloc_bufs(s); -#ifdef IPV6 -#ifdef IPV6_MTU_DISCOVER - { - int dont = IPV6_PMTUDISC_DONT; - if (setsockopt(fd, SOL_IPV6, IPV6_MTU_DISCOVER, &dont, sizeof(dont)) < 0) - ERR("IPV6_MTU_DISCOVER"); - } -#endif -#else -#ifdef IP_PMTUDISC - { - int dont = IP_PMTUDISC_DONT; - if (setsockopt(fd, SOL_IP, IP_PMTUDISC, &dont, sizeof(dont)) < 0) - ERR("IP_PMTUDISC"); - } -#endif -#endif - } + { + case SK_TCP_ACTIVE: + sockaddr_fill(&sa, af, s->daddr, s->iface, s->dport); + if (connect(fd, &sa.sa, SA_LEN(sa)) >= 0) + sk_tcp_connected(s); + else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && + errno != ECONNREFUSED && errno != EHOSTUNREACH && errno != ENETUNREACH) + ERR2("connect"); + break; + + case SK_TCP_PASSIVE: + if (listen(fd, 8) < 0) + ERR2("listen"); + break; + + case SK_MAGIC: + break; + + default: + sk_alloc_bufs(s); + } if (!(s->flags & SKF_THREAD)) sk_insert(s); return 0; -bad: - log(L_ERR "sk_open: %s: %m", err); -bad_no_log: +err: close(fd); s->fd = -1; return -1; } -void +int sk_open_unix(sock *s, char *name) { - int fd; struct sockaddr_un sa; - char *err; + 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) - ERR("socket"); - s->fd = fd; - if (err = sk_setup(s)) - goto bad; - unlink(name); + return -1; + + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + return -1; /* Path length checked in test_old_bird() */ sa.sun_family = AF_UNIX; strcpy(sa.sun_path, name); - if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) < 0) - ERR("bind"); - if (listen(fd, 8)) - ERR("listen"); - sk_insert(s); - return; - bad: - log(L_ERR "sk_open_unix: %s: %m", err); - die("Unable to create control socket %s", 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; +} + + +#define CMSG_RX_SPACE MAX(CMSG4_SPACE_PKTINFO+CMSG4_SPACE_TTL, \ + CMSG6_SPACE_PKTINFO+CMSG6_SPACE_TTL) +#define CMSG_TX_SPACE MAX(CMSG4_SPACE_PKTINFO,CMSG6_SPACE_PKTINFO) + +static void +sk_prepare_cmsgs(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen) +{ + if (sk_is_ipv4(s)) + sk_prepare_cmsgs4(s, msg, cbuf, cbuflen); + else + sk_prepare_cmsgs6(s, msg, cbuf, cbuflen); +} + +static void +sk_process_cmsgs(sock *s, struct msghdr *msg) +{ + struct cmsghdr *cm; + + s->laddr = IPA_NONE; + s->lifindex = 0; + s->rcv_ttl = -1; + + for (cm = CMSG_FIRSTHDR(msg); cm != NULL; cm = CMSG_NXTHDR(msg, cm)) + { + if ((cm->cmsg_level == SOL_IP) && sk_is_ipv4(s)) + { + sk_process_cmsg4_pktinfo(s, cm); + sk_process_cmsg4_ttl(s, cm); + } + + if ((cm->cmsg_level == SOL_IPV6) && sk_is_ipv6(s)) + { + sk_process_cmsg6_pktinfo(s, cm); + sk_process_cmsg6_ttl(s, cm); + } + } } @@ -1362,11 +1494,11 @@ sk_sendmsg(sock *s) byte cmsg_buf[CMSG_TX_SPACE]; sockaddr dst; - fill_in_sockaddr(&dst, s->daddr, s->iface, s->dport); + sockaddr_fill(&dst, s->af, s->daddr, s->iface, s->dport); struct msghdr msg = { - .msg_name = &dst, - .msg_namelen = sizeof(dst), + .msg_name = &dst.sa, + .msg_namelen = SA_LEN(dst), .msg_iov = &iov, .msg_iovlen = 1 }; @@ -1377,14 +1509,14 @@ sk_sendmsg(sock *s) if (s->flags & SKF_HDRINCL) { - fill_ip_header(s, hdr, iov.iov_len); + sk_prepare_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)); + sk_prepare_cmsgs(s, &msg, cmsg_buf, sizeof(cmsg_buf)); return sendmsg(s->fd, &msg, 0); } @@ -1397,8 +1529,8 @@ sk_recvmsg(sock *s) sockaddr src; struct msghdr msg = { - .msg_name = &src, - .msg_namelen = sizeof(src), + .msg_name = &src.sa, + .msg_namelen = sizeof(src), // XXXX ?? .msg_iov = &iov, .msg_iovlen = 1, .msg_control = cmsg_buf, @@ -1415,8 +1547,8 @@ sk_recvmsg(sock *s) // rv = ipv4_skip_header(pbuf, rv); //endif - get_sockaddr(&src, &s->faddr, NULL, &s->fport, 1); - sysio_process_rx_cmsgs(s, &msg); + sockaddr_read(&src, s->af, &s->faddr, NULL, &s->fport); + sk_process_cmsgs(s, &msg); if (msg.msg_flags & MSG_TRUNC) s->flags |= SKF_TRUNCATED; @@ -1435,55 +1567,57 @@ sk_maybe_write(sock *s) int e; switch (s->type) + { + case SK_TCP: + case SK_MAGIC: + case SK_UNIX: + while (s->ttx != s->tpos) { - case SK_TCP: - case SK_MAGIC: - case SK_UNIX: - while (s->ttx != s->tpos) + e = write(s->fd, s->ttx, s->tpos - s->ttx); + + if (e < 0) + { + if (errno != EINTR && errno != EAGAIN) { - e = write(s->fd, s->ttx, s->tpos - s->ttx); - if (e < 0) - { - if (errno != EINTR && errno != EAGAIN) - { - reset_tx_buffer(s); - /* EPIPE is just a connection close notification during TX */ - s->err_hook(s, (errno != EPIPE) ? errno : 0); - return -1; - } - return 0; - } - s->ttx += e; + reset_tx_buffer(s); + /* EPIPE is just a connection close notification during TX */ + s->err_hook(s, (errno != EPIPE) ? errno : 0); + return -1; } + return 0; + } + s->ttx += e; + } + reset_tx_buffer(s); + return 1; + + case SK_UDP: + case SK_IP: + { + if (s->tbuf == s->tpos) + return 1; + + e = sk_sendmsg(s); + + if (e < 0) + { + if (errno != EINTR && errno != EAGAIN) + { + reset_tx_buffer(s); + s->err_hook(s, errno); + return -1; + } + + if (!s->tx_hook) + reset_tx_buffer(s); + return 0; + } reset_tx_buffer(s); return 1; - case SK_UDP: - case SK_IP: - { - if (s->tbuf == s->tpos) - return 1; - - e = sk_sendmsg(s); - - if (e < 0) - { - if (errno != EINTR && errno != EAGAIN) - { - reset_tx_buffer(s); - s->err_hook(s, errno); - return -1; - } - - if (!s->tx_hook) - reset_tx_buffer(s); - return 0; - } - reset_tx_buffer(s); - return 1; - } - default: - bug("sk_maybe_write: unknown socket type %d", s->type); } + default: + bug("sk_maybe_write: unknown socket type %d", s->type); + } } int @@ -1573,88 +1707,86 @@ int sk_read(sock *s) { switch (s->type) + { + case SK_TCP_PASSIVE: + return sk_passive_connected(s, SK_TCP); + + case SK_UNIX_PASSIVE: + return sk_passive_connected(s, SK_UNIX); + + case SK_TCP: + case SK_UNIX: { - case SK_TCP_PASSIVE: + int c = read(s->fd, s->rpos, s->rbuf + s->rbsize - s->rpos); + + if (c < 0) { - sockaddr sa; - return sk_passive_connected(s, (struct sockaddr *) &sa, sizeof(sa), SK_TCP); + if (errno != EINTR && errno != EAGAIN) + s->err_hook(s, errno); } - case SK_UNIX_PASSIVE: + else if (!c) + s->err_hook(s, 0); + else { - struct sockaddr_un sa; - return sk_passive_connected(s, (struct sockaddr *) &sa, sizeof(sa), SK_UNIX); - } - case SK_TCP: - case SK_UNIX: - { - int c = read(s->fd, s->rpos, s->rbuf + s->rbsize - s->rpos); - - if (c < 0) - { - if (errno != EINTR && errno != EAGAIN) - s->err_hook(s, errno); - } - else if (!c) - s->err_hook(s, 0); - else - { - s->rpos += c; - if (s->rx_hook(s, s->rpos - s->rbuf)) - { - /* We need to be careful since the socket could have been deleted by the hook */ - if (current_sock == s) - s->rpos = s->rbuf; - } - return 1; - } - return 0; - } - case SK_MAGIC: - return s->rx_hook(s, 0); - default: - { - int e; - - e = sk_recvmsg(s); - - if (e < 0) - { - if (errno != EINTR && errno != EAGAIN) - s->err_hook(s, errno); - return 0; - } - - s->rpos = s->rbuf + e; - s->rx_hook(s, e); + s->rpos += c; + if (s->rx_hook(s, s->rpos - s->rbuf)) + { + /* We need to be careful since the socket could have been deleted by the hook */ + if (current_sock == s) + s->rpos = s->rbuf; + } return 1; } + return 0; } + + case SK_MAGIC: + return s->rx_hook(s, 0); + + default: + { + int e = sk_recvmsg(s); + + if (e < 0) + { + if (errno != EINTR && errno != EAGAIN) + s->err_hook(s, errno); + return 0; + } + + s->rpos = s->rbuf + e; + s->rx_hook(s, e); + return 1; + } + } } int sk_write(sock *s) { switch (s->type) + { + case SK_TCP_ACTIVE: { - case SK_TCP_ACTIVE: - { - sockaddr sa; - fill_in_sockaddr(&sa, s->daddr, s->iface, s->dport); - if (connect(s->fd, (struct sockaddr *) &sa, sizeof(sa)) >= 0 || errno == EISCONN) - sk_tcp_connected(s); - else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) - s->err_hook(s, errno); - return 0; - } - default: - if (s->ttx != s->tpos && sk_maybe_write(s) > 0) - { - if (s->tx_hook) - s->tx_hook(s); - return 1; - } + sockaddr sa; + sockaddr_fill(&sa, s->af, s->daddr, s->iface, s->dport); + + if (connect(s->fd, &sa.sa, SA_LEN(sa)) >= 0 || errno == EISCONN) + sk_tcp_connected(s); + else if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS) + s->err_hook(s, errno); return 0; } + + default: + if (s->ttx != s->tpos && sk_maybe_write(s) > 0) + { + if (s->tx_hook) + s->tx_hook(s); + return 1; + } + return 0; + } } void @@ -1665,16 +1797,14 @@ sk_dump_all(void) debug("Open sockets:\n"); WALK_LIST(n, sock_list) - { - s = SKIP_BACK(sock, n, n); - debug("%p ", s); - sk_dump(&s->r); - } + { + s = SKIP_BACK(sock, n, n); + debug("%p ", s); + sk_dump(&s->r); + } debug("\n"); } -#undef ERR -#undef WARN /* * Main I/O Loop diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 31094c52..61b306dc 100644 --- a/sysdep/unix/main.c +++ b/sysdep/unix/main.c @@ -463,7 +463,12 @@ cli_init_unix(uid_t use_uid, gid_t use_gid) s->type = SK_UNIX_PASSIVE; s->rx_hook = cli_connect; s->rbsize = 1024; - sk_open_unix(s, path_control_socket); + + /* Return value intentionally ignored */ + unlink(path_control_socket); + + if (sk_open_unix(s, path_control_socket) < 0) + die("Cannot create control socket %s: %m", path_control_socket); if (use_uid || use_gid) if (chown(path_control_socket, use_uid, use_gid) < 0) diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h index 346adcf2..518713bc 100644 --- a/sysdep/unix/unix.h +++ b/sysdep/unix/unix.h @@ -12,6 +12,8 @@ #include struct pool; +struct iface; +struct birdsock; /* main.c */ @@ -27,36 +29,81 @@ void cmd_shutdown(void); #define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300 + /* io.c */ -volatile int async_config_flag; -volatile int async_dump_flag; -volatile int async_shutdown_flag; +#define ERR(c) do { s->err = c; return -1; } while (0) +#define ERR2(c) do { s->err = c; goto err; } while (0) +#define ERR_MSG(c) do { errno = 0; s->err = c; return -1; } while (0) + + +#define SOCKADDR_SIZE 32 + +typedef struct sockaddr_bird { + struct sockaddr sa; + char padding[SOCKADDR_SIZE - sizeof(struct sockaddr)]; +} sockaddr; + #ifdef IPV6 -#define BIRD_PF PF_INET6 #define BIRD_AF AF_INET6 -typedef struct sockaddr_in6 sockaddr; -static inline int sa_family_check(sockaddr *sa) { return sa->sin6_family == AF_INET6; } +#define _MI6(x1,x2,x3,x4) _MI(x1, x2, x3, x4) +#define ipa_is_link_local(x) ipa_has_link_scope(x) +#define ipa_from_sa(x) ipa_from_sa6(x) +#define ipa_from_u32(x) _MI6(0,0,0xffff,x) +#define ipa_to_u32(x) _I3(x) #else -#define BIRD_PF PF_INET #define BIRD_AF AF_INET -typedef struct sockaddr_in sockaddr; -static inline int sa_family_check(sockaddr *sa) { return sa->sin_family == AF_INET; } +#define _I0(X) 0 +#define _I1(X) 0 +#define _I2(X) 0 +#define _I3(X) 0 +#define _MI6(x1,x2,x3,x4) IPA_NONE +#define ipa_is_link_local(x) 0 +#define ipa_from_sa(x) ipa_from_sa4(x) #endif + +/* 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 +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + + +static inline ip_addr ipa_from_in4(struct in_addr a) +{ return ipa_from_u32(ntohl(a.s_addr)); } + +static inline ip_addr ipa_from_in6(struct in6_addr a) +{ return _MI6(ntohl(a.s6_addr32[0]), ntohl(a.s6_addr32[1]), ntohl(a.s6_addr32[2]), ntohl(a.s6_addr32[3])); } + +static inline ip_addr ipa_from_sa4(sockaddr *sa) +{ return ipa_from_in4(((struct sockaddr_in *) sa)->sin_addr); } + +static inline ip_addr ipa_from_sa6(sockaddr *sa) +{ return ipa_from_in6(((struct sockaddr_in6 *) sa)->sin6_addr); } + +static inline struct in_addr ipa_to_in4(ip_addr a) +{ return (struct in_addr) { htonl(ipa_to_u32(a)) }; } + +static inline struct in6_addr ipa_to_in6(ip_addr a) +{ return (struct in6_addr) { .s6_addr32 = { htonl(_I0(a)), htonl(_I1(a)), htonl(_I2(a)), htonl(_I3(a)) } }; } + +void sockaddr_fill(sockaddr *sa, int af, ip_addr a, struct iface *ifa, uint port); +int sockaddr_read(sockaddr *sa, int af, ip_addr *a, struct iface **ifa, uint *port); + + #ifndef SUN_LEN #define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) + strlen ((ptr)->sun_path)) #endif -struct birdsock; -struct iface; +volatile int async_config_flag; +volatile int async_dump_flag; +volatile int async_shutdown_flag; void io_init(void); void io_loop(void); -void fill_in_sockaddr(sockaddr *sa, ip_addr a, struct iface *ifa, unsigned port); -void get_sockaddr(sockaddr *sa, ip_addr *a, struct iface **ifa, unsigned *port, int check); -void sk_open_unix(struct birdsock *s, char *name); +int sk_open_unix(struct birdsock *s, char *name); void *tracked_fopen(struct pool *, char *name, char *mode); void test_old_bird(char *path);