mirror of
				https://gitlab.labs.nic.cz/labs/bird.git
				synced 2024-05-11 16:54:54 +00:00 
			
		
		
		
	Add option to socket interface for nonlocal binding, i.e. binding to an IP address that is not present on interfaces. This behaviour is enabled when SKF_FREEBIND socket flag is set. For Linux systems, it is implemented by IP_FREEBIND socket flag. Minor changes done by commiter.
		
			
				
	
	
		
			288 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *	BIRD Internet Routing Daemon -- Linux Multicasting and Network Includes
 | |
|  *
 | |
|  *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
 | |
|  *
 | |
|  *	Can be freely distributed and used under the terms of the GNU GPL.
 | |
|  */
 | |
| 
 | |
| #ifndef IPV6_MINHOPCOUNT
 | |
| #define IPV6_MINHOPCOUNT 73
 | |
| #endif
 | |
| 
 | |
| #ifndef IPV6_FREEBIND
 | |
| #define IPV6_FREEBIND 78
 | |
| #endif
 | |
| 
 | |
| #ifndef TCP_MD5SIG_EXT
 | |
| #define TCP_MD5SIG_EXT 32
 | |
| #endif
 | |
| 
 | |
| #ifndef TCP_MD5SIG_FLAG_PREFIX
 | |
| #define TCP_MD5SIG_FLAG_PREFIX 1
 | |
| #endif
 | |
| 
 | |
| /* We redefine the tcp_md5sig structure with different name to avoid collision with older headers */
 | |
| struct tcp_md5sig_ext {
 | |
|   struct  sockaddr_storage tcpm_addr;		/* Address associated */
 | |
|   u8    tcpm_flags;				/* Extension flags */
 | |
|   u8    tcpm_prefixlen;				/* Address prefix */
 | |
|   u16   tcpm_keylen;				/* Key length */
 | |
|   u32   __tcpm_pad2;				/* Zero */
 | |
|   u8    tcpm_key[TCP_MD5SIG_MAXKEYLEN];		/* Key (binary) */
 | |
| };
 | |
| 
 | |
| 
 | |
| /* 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 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");
 | |
| 
 | |
|   if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
 | |
|     ERR("IP_MULTICAST_TTL");
 | |
| 
 | |
|   if (setsockopt(s->fd, SOL_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_mreqn mr = INIT_MREQ4(maddr, s->iface);
 | |
| 
 | |
|   if (setsockopt(s->fd, SOL_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_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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  *	Linux IPv4 packet control messages
 | |
|  */
 | |
| 
 | |
| /* Mostly similar to standardized IPv6 code */
 | |
| 
 | |
| #define CMSG4_SPACE_PKTINFO CMSG_SPACE(sizeof(struct in_pktinfo))
 | |
| #define CMSG4_SPACE_TTL CMSG_SPACE(sizeof(int))
 | |
| 
 | |
| static inline int
 | |
| sk_request_cmsg4_pktinfo(sock *s)
 | |
| {
 | |
|   int y = 1;
 | |
| 
 | |
|   if (setsockopt(s->fd, SOL_IP, IP_PKTINFO, &y, sizeof(y)) < 0)
 | |
|     ERR("IP_PKTINFO");
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static inline int
 | |
| sk_request_cmsg4_ttl(sock *s)
 | |
| {
 | |
|   int y = 1;
 | |
| 
 | |
|   if (setsockopt(s->fd, SOL_IP, IP_RECVTTL, &y, sizeof(y)) < 0)
 | |
|     ERR("IP_RECVTTL");
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   int controllen = 0;
 | |
| 
 | |
|   msg->msg_control = cbuf;
 | |
|   msg->msg_controllen = cbuflen;
 | |
| 
 | |
|   cm = CMSG_FIRSTHDR(msg);
 | |
|   cm->cmsg_level = SOL_IP;
 | |
|   cm->cmsg_type = IP_PKTINFO;
 | |
|   cm->cmsg_len = CMSG_LEN(sizeof(*pi));
 | |
|   controllen += CMSG_SPACE(sizeof(*pi));
 | |
| 
 | |
|   pi = (struct in_pktinfo *) CMSG_DATA(cm);
 | |
|   pi->ipi_ifindex = s->iface ? s->iface->index : 0;
 | |
|   pi->ipi_spec_dst = ipa_to_in4(s->saddr);
 | |
|   pi->ipi_addr = ipa_to_in4(IPA_NONE);
 | |
| 
 | |
|   msg->msg_controllen = controllen;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  *	Miscellaneous Linux socket syscalls
 | |
|  */
 | |
| 
 | |
| int
 | |
| sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey UNUSED)
 | |
| {
 | |
|   struct tcp_md5sig_ext md5;
 | |
| 
 | |
|   memset(&md5, 0, sizeof(md5));
 | |
|   sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, remote, ifa, 0);
 | |
| 
 | |
|   if (passwd)
 | |
|   {
 | |
|     int len = strlen(passwd);
 | |
| 
 | |
|     if (len > TCP_MD5SIG_MAXKEYLEN)
 | |
|       ERR_MSG("The password for TCP MD5 Signature is too long");
 | |
| 
 | |
|     md5.tcpm_keylen = len;
 | |
|     memcpy(&md5.tcpm_key, passwd, len);
 | |
|   }
 | |
| 
 | |
|   if (pxlen < 0)
 | |
|   {
 | |
|     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");
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     md5.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX;
 | |
|     md5.tcpm_prefixlen = pxlen;
 | |
| 
 | |
|     if (setsockopt(s->fd, SOL_TCP, TCP_MD5SIG_EXT, &md5, sizeof(md5)) < 0)
 | |
|     {
 | |
|       if (errno == ENOPROTOOPT)
 | |
| 	ERR_MSG("Kernel does not support extended TCP MD5 signatures");
 | |
|       else
 | |
| 	ERR("TCP_MD5SIG_EXT");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   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)
 | |
|       ERR_MSG("Kernel does not support IPv4 TTL security");
 | |
|     else
 | |
|       ERR("IP_MINTTL");
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 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)
 | |
|       ERR_MSG("Kernel does not support IPv6 TTL security");
 | |
|     else
 | |
|       ERR("IPV6_MINHOPCOUNT");
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| 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");
 | |
| 
 | |
|   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 inline int
 | |
| sk_set_priority(sock *s, int prio)
 | |
| {
 | |
|   if (setsockopt(s->fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
 | |
|     ERR("SO_PRIORITY");
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static inline int
 | |
| sk_set_freebind(sock *s)
 | |
| {
 | |
|   int y = 1;
 | |
| 
 | |
|   if (sk_is_ipv4(s))
 | |
|     if (setsockopt(s->fd, SOL_IP, IP_FREEBIND, &y, sizeof(y)) < 0)
 | |
|       ERR("IP_FREEBIND");
 | |
| 
 | |
|   if (sk_is_ipv6(s))
 | |
|     if (setsockopt(s->fd, SOL_IPV6, IPV6_FREEBIND, &y, sizeof(y)) < 0)
 | |
|       ERR("IPV6_FREEBIND");
 | |
| 
 | |
|   return 0;
 | |
| }
 |