Binding to fe80::%<scope> - c

Trying to get daemon to bind to fe80::%en0.
I'm not 100% sure this is possible, but it really seems like for the link-local address to be useful it should be.
If you want to accept packets from your peer on the other side of the link, it seems stupid to have to specify the full link-local address.
The code looks like this:
if (DEBUG_ENABLED4) {
if (salocal.ss_family == AF_INET) {
char buffer[INET_ADDRSTRLEN];
struct sockaddr_in *addr = (struct sockaddr_in *)&salocal;
inet_ntop(addr->sin_family, &addr->sin_addr, buffer, sizeof(buffer));
DEBUG4("[FD %i] Binding to address %s port %u -- bind(%i, %p, %u)",
this->fd, buffer, ntohs(addr->sin_port), this->fd, &salocal, salen);
} else if (salocal.ss_family == AF_INET6) {
char buffer[INET6_ADDRSTRLEN];
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&salocal;
inet_ntop(addr->sin6_family, &addr->sin6_addr, buffer, sizeof(buffer));
DEBUG4("[FD %i] Binding to address %s port %u scope ID %i -- bind(%i, %p, %u)",
this->fd, buffer, ntohs(addr->sin6_port), addr->sin6_scope_id,
this->fd, &salocal, salen);
}
}
rad_suid_up();
rcode = bind(this->fd, (struct sockaddr *)&salocal, salen);
rad_suid_down();
The debug output looks like this:
Sat Nov 7 19:58:28 2015 : Debug: [FD 12] Binding to address fe80:: port 1812 scope ID 4 -- bind(12, 0x7fff599dc4d0, 28)
Sat Nov 7 19:58:28 2015 : Error: Failed binding to auth address fe80:: port 1812 bound to server default: EADDRNOTAVAIL: Can't assign requested address
Sat Nov 7 19:58:28 2015 : Error: /usr/local/freeradius/etc/raddb/sites-enabled/default[215]: Error binding to port for fe80:: port 1812
Ifconfig output looks like this:
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
ether a8:20:66:xx:xx:xx
inet6 fe80::aa20:66ff:fe1a:8bcd%en0 prefixlen 64 scopeid 0x4
Values all seem to match up, but bind returns EADDRNOTAVAIL. Is there some magic socket option I need to enable to make this work? Or do I need to check for that link local IP prefix specifically and then resolve it to the full address for the interface described by the scope_id/zone_id?

In short: if you want to bind to a specific address you'll have to specify that address. A host can have multiple link-local addresses. If you want to bind to all link-local addresses automatically you'll have to auto-discover them and create a socket for each of them.

Related

sendmsg() / connect() fails with permission error/EACCES on raw IPv6 socket

I try to get a program running on OpenWRT (kernel ver.: 4.14.215, musl libc ver.: 1.1.24) which implements RFC8157 (a new tunneling protocol). Unfortunately the guy who wrote it doesn't seem to maintain it anymore.
At some point it writes its first message to a raw ipv6 socket via sendmsg(). Unfortunately sendmsg() returns EACCES. I am pretty new to system programming and do not really have a clue what to look for.
I have tried the following:
#> ls -l /proc/[pid]/fd/*
lrwx------ 1 root root 64 Jan 25 17:41 /proc/22727/fd/0 -> /dev/pts/0
lrwx------ 1 root root 64 Jan 25 17:41 /proc/22727/fd/1 -> /dev/pts/0
lrwx------ 1 root root 64 Jan 25 17:41 /proc/22727/fd/2 -> /dev/pts/0
lrwx------ 1 root root 64 Jan 25 17:41 /proc/22727/fd/3 -> socket:[1293688]
#> ls -l /proc/[pid]/fdinfo/*
pos: 0
flags: 02
mnt_id: 8
So the socket seems to be opened in read/write mode.
lsof lists the socket as well. But for some reason with an ipv6 address of 0.
#> lsof | grep [pid]
openhybri 18018 root 3u raw6 0t0 92469 00000000000000000000000000000000:002F->00000000000000000000000000000000:0000 st=07
The man page lists the attempt to send an UDP packet from a broadcast address to an anycast address as possible cause. But this seems not the case here. A raw IPv6 socket is not a UDP socket (isn't it?) and the src IP is a public one.
Everything is executed as root user.
#> id
uid=0(root) gid=0(root) groups=0(root)
As I am not really sure what to look for, here is the whole function:
sendmsg() is used in the last if statement.
bool send_grecpmessage(uint8_t msgtype, uint8_t tuntype, void *attributes, int attributes_size) {
unsigned char buffer[MAX_PKT_SIZE] = {};
int size = 0;
/* GRE header */
struct grehdr *greh = (struct grehdr *)(buffer + size);
greh->flags_and_version = htons(GRECP_FLAGSANDVERSION);
greh->proto = htons(GRECP_PROTO);
greh->key = htonl(runtime.haap.bonding_key);
size += sizeof(struct grehdr);
/* GRECP header */
struct grecphdr *grecph = (struct grecphdr *)(buffer + size);
grecph->msgtype_and_tuntype = (msgtype << 4) | tuntype;
size += sizeof(struct grecphdr);
/* Add GRECP attributes */
memcpy(buffer + size, attributes, attributes_size);
size += attributes_size;
/* Source & Destination */
struct sockaddr_in6 src = {};
src.sin6_family = AF_INET6;
if (tuntype == GRECP_TUNTYPE_LTE) {
src.sin6_addr = runtime.lte.interface_ip;
} else {
src.sin6_addr = runtime.dsl.interface_ip;
}
struct sockaddr_in6 dst = {};
dst.sin6_family = AF_INET6;
dst.sin6_addr = runtime.haap.ip;
/* Construct control information */
struct msghdr msgh = {};
struct iovec msgiov = {};
struct cmsghdr *c;
struct unp_in_pktinfo {
struct in6_addr ipi6_addr;
int ipi6_ifindex;
} *pi;
msgh.msg_name = &dst;
msgh.msg_namelen = sizeof(struct sockaddr_in6);
msgiov.iov_base = buffer;
msgiov.iov_len = size;
msgh.msg_iov = &msgiov;
msgh.msg_iovlen = 1;
unsigned char control_buf[CMSG_LEN(sizeof(struct unp_in_pktinfo))] = {};
msgh.msg_control = &control_buf;
msgh.msg_controllen = CMSG_LEN(sizeof(struct unp_in_pktinfo));
c = CMSG_FIRSTHDR(&msgh);
c->cmsg_level = IPPROTO_IPV6;
c->cmsg_type = IPV6_PKTINFO;
c->cmsg_len = CMSG_LEN(sizeof(struct unp_in_pktinfo));
pi = (struct unp_in_pktinfo *)CMSG_DATA(c);
pi->ipi6_addr = src.sin6_addr;
msgh.msg_controllen = c->cmsg_len;
bool res = true;
if (memcmp(&src.sin6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) != 0) {
if (sendmsg(sockfd, &msgh, 0) <= 0) {
logger(LOG_ERROR, "Raw socket send failed: %s\n", strerror(errno));
res = false;
}
} else {
/* if we don't set a source ip, sendmsg() will use the ip of the outgoing interface
** and since the haap doesn't verify source ip's we would still get replies for our hellos
*/
res = false;
}
/* TODO: check if sending failed due to a link failure and call send_grecpnotify_linkfailure if it did */
return res;
}
You need root (or a sufficient subset as capabilities) to perform raw packet io. This is because ability to construct and send or capture arbitrary packets allows you to spoof or intercept traffic that's part of a connection belonging to another user or system network facilities. EACCES is telling you that you don't have sufficient permissions.
The problem was that the ip route wasn't set for the specific address I tried to reach.
ip route add [IP] via [gateway] dev [interface]
solved it.

eBPF: How can the sockaddr struct be read when hooking into security_socket_connect

As proposed in the presentation Security Monitoring with eBPF I'm trying to hook into security_socket_connect.
While my gobpf/bcc based code partly works, I seem not be able to read the IP address in the sockaddr struct.
The relevant part looks like this:
int security_socket_connect_entry(struct pt_regs *ctx, struct socket *sock, struct sockaddr *address, int addrlen)
{
u32 address_family = address->sa_family;
if (address_family == AF_INET) {
struct ipv4_data_t data4 = {.pid = pid};
struct sockaddr_in *addr2 = (struct sockaddr_in *)address;
After that I try to read the IP address in addr2. The first try was:
data4.daddr = addr2->sin_addr.s_addr;
The second try was with bpf_probe_read:
bpf_probe_read(&data4.daddr, sizeof(data4.daddr), (void *)((long)addr2->sin_addr.s_addr));
Both options present the same error:
R9 invalid mem access 'inv'
HINT: The invalid mem access 'inv' error can happen if you try to dereference memory without first using bpf_probe_read() to copy it to the BPF stack. Sometimes the bpf_probe_read is automatic by the bcc rewriter, other times you'll need to be explicit.
A repo with a buildable sample can be found here: socket-connect-bpf
I figured it out thanks to an answer to issue #1858 in the bcc repo.
We have to operate on the pointer, so the IP address can be read like this:
bpf_probe_read(&data4.daddr, sizeof(data4.daddr), &addr2->sin_addr.s_addr);

Mismatch in assigning IP address to a buffer and printing contents of buffer

I am implementing raw sockets in C. In the code below, I am parsing the IP header I received from sender.
a. I will send back the ack as well so storing IP address received in a buffer(ret_ip).
b. I don't have another computer so using lo (local loop back) as my interface.
//first two printf statements are printing the right address, 10.100.207.74
//daddr SENT = 32.112.247.9saddr SENT = 36.112.247.9
How can I get it correct?
I think this problem is due to memcpy whose first argument is pointing to unsigned char while second argument is pointing to _be32.
What I actually want to do in my program is : ret_ip's first 4 bytes should contain the destination address and next 4 the source address. Then I will create IP header and make dest addr=source addr and source-addr=dest-addr. and send ACK to sender.
char* ParseIPHeader(unsigned char *packet,int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
char *ret_ip;
ethernet_header=(struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
printf("Dest IP address: %s\n",inet_ntoa(ip_header->daddr));
printf("Source IP address: %s\n",inet_ntoa(ip_header->saddr));
memcpy(ret_ip,&(ip_header->daddr),sizeof(ip_header->daddr));
memcpy(ret_ip+4,&(ip_header->saddr),4);
printf("daddr SENT = %s",inet_ntoa(ret_ip));
printf("saddr SENT = %s",inet_ntoa(ret_ip+4));
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return ret_ip;
}
Thanks :)
first problem is your memory allocation
ret_ip=malloc(2*(ip_header->daddr));
it should be
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
but why you are not using the ip_hdr struct again ? for example
struct iphdr *ret_ip = malloc(sizeof(iphdr));
ret_ip->daddr = ip_header->saddr;
ret_ip->saddr = ip_header->daddr;
i suggest this solution is much easier ;)

Get ip-address of local NIC from kernel module

I am writing a kernel module which forms its own packet at ip level and then sends the data . I just want to know how to find the ip address of a local network interface.
There are defined ioctls SIOCGIFADDR (to get if address) and SIOCSIFADDR (to set if address).
All device configuration for IPv4 is done in net/ipv4/devinet.c. You can refer to this source file to get more information.
Wanting to read interface addresses reeks of a design problem. However, if you are looking for determining the preferred source address to use when contacting a remote peer is given in struct rt6_info.rt6i_prefsrc after obtaining it with ip6_route_output. Something along the lines of (with no implied guarantees):
int pick_addr(struct in6_addr *saddr, struct net *net, const struct in6_addr *daddr)
{
struct rt6_info *rt;
struct flowi6 fl6;
int ret;
memset(&fl6, 0, sizeof(fl6));
memcpy(&fl6.daddr, daddr, sizeof(*daddr));
dst = (struct rt6_info *)ip6_route_output(net, NULL /* or sk if you have it */, &fl6);
ret = rt->dst.error;
if (ret == 0)
memcpy(saddr, &rt->rt6i_prefsrc.addr, sizeof(*saddr));
dst_release(&rt->dst);
return ret;
}
We have if_getconfig function available in linux-x.y.z/Documentation/networking/ifenslave.c file which is a very good example of how to use ioctls and fetch address from kernel space

Is this a bug in inet_pton with IPv6?

When trying to resolve Facebook's numeric IP address as a test 2620:0:1cfe:face:b00c::3:, if I leave the terminating 0 off the address, inet_pton() barfs. If I put it back on everything works.
Running ubuntu 9.10:
rc = inet_pton(AF_INET6, "2620:0:1cfe:face:b00c::3:0", &ip); -> OK
rc = inet_pton(AF_INET6, "2620:0:1cfe:face:b00c::3:", &ip); -> returns -2
ping6 -n www.v6.facebook.com returns the IP address w/o the trailing 0.
It seems that ping, in it's great wisdom, adds a colon after the IP address like so:
PING maclawran.ca (173.230.128.18) 56(84) bytes of data.
64 bytes from ns.maclawran.ca (173.230.128.18): icmp_seq=1 ttl=51 time=50.3 ms
Of course if you're pinging an IPv6 address, it's already got lots of colons in it:
PING 2620:0:1cfe:face:b00c::3(2620:0:1cfe:face:b00c::3) 56 data bytes
64 bytes from 2620:0:1cfe:face:b00c::3: icmp_seq=1 ttl=52 time=9.44 ms
======================================^ << THANKS PING

Resources