Binding a BPF descriptor to an interface - c

I'm trying to write a small C program on an OpenBSD vm I just installed, and cannot get my file descriptor (to /dev/bpf) to bind to my interface.
OpenBSD info:
openbsd:waffles uplime$ uname -a
OpenBSD openbsd.securitea.app 6.5 GENERIC#3 amd64
openbsd:waffles uplime$ ifconfig
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 32768
index 4 priority 0 llprio 3
groups: lo
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4
inet 127.0.0.1 netmask 0xff000000
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:0c:29:34:99:7b
index 1 priority 0 llprio 3
groups: egress
media: Ethernet autoselect (1000baseT full-duplex,master)
status: active
inet 10.10.73.199 netmask 0xfffffc00 broadcast 10.10.75.255
em1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:0c:29:34:99:85
index 2 priority 0 llprio 3
media: Ethernet autoselect (1000baseT full-duplex,master)
status: active
inet 172.16.221.129 netmask 0xffffff00 broadcast 172.16.221.255
enc0: flags=0<>
index 3 priority 0 llprio 3
groups: enc
status: active
pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33136
index 5 priority 0 llprio 3
groups: pflog
openbsd:waffles uplime$
However, when I try to bind my descriptor to em0, I get the following error:
openbsd:waffles uplime$ cc -std=c99 -Wall -Wextra -pedantic -g -O1 -o tmp/test tmp/test.c
openbsd:waffles uplime$ doas tmp/test
ioctl: em0: Device not configured
openbsd:waffles uplime$
I'm using the following testcase:
#include <errno.h>
#include <fcntl.h>
#include <net/bpf.h>
#include <net/if.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main() {
int fd = open("/dev/bpf", O_RDONLY);
int packet_max = 32768;
if(fd < 0) {
fprintf(stderr, "open: /dev/bpf: %s\n", strerror(errno));
return 1;
}
if(ioctl(fd, BIOCSBLEN, &packet_max) < 0) {
fprintf(stderr, "ioctl: BIOCSBLEN: %s\n", strerror(errno));
return 1;
}
struct ifreq req;
memset(&req, 0, sizeof(req));
strcpy(req.ifr_name, "em0");
if(ioctl(fd, BIOCSETIF, req) < 0) {
fprintf(stderr, "ioctl: em0: %s\n", strerror(errno));
return 1;
}
return 0;
}
I know the interface is fine, because it is the only one connected to the internet, and I'm able to make inbound/outbound requests without issue. I can also run tcpdump -i em0 without issue. Am I missing an ioctl call (or something similar) to make the interface usable?

Related

setsockopt fails while kernel TLS option enabling

I am learning about kernel TLS on kernel 5.3 version (Fedora 30 and 31) but stuck even on enabling ULP:
// tls.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/tls.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
int main() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
perror("socket creation");
exit (EXIT_FAILURE);
}
if (setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls")) == -1 ) {
perror("tls init");
exit (EXIT_FAILURE);
}
close (sock);
return EXIT_SUCCESS;
}
Then:
$ cat /proc/sys/net/ipv4/tcp_available_ulp
tls
$ gcc tls.c -O0 -g
$ lsmod | grep tls
$ ./a.out
tls init: No such file or directory
$ lsmod | grep tls
$ sudo ./a.out
tls init: Unknown error 524
$ lsmod | grep tls
tls 57344 0
$ ./a.out
tls init: Unknown error 524
$
I don't know where I am wrong. It looks so simple in Kernel TLS docs:
Creating a TLS connection
First create a new TCP socket and set the TLS ULP.
sock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(sock, SOL_TCP, TCP_ULP, "tls", sizeof("tls"));
Setting the TLS ULP allows us to set/get TLS socket options. Currently
only the symmetric encryption is handled in the kernel. After the TLS
handshake is complete, we have all the parameters required to move the
data-path to the kernel. There is a separate socket option for moving
the transmit and the receive into the kernel.
I don't understand how I can enable in kernel TLS and what I am doing wrong.

sctp_connectx() gives EINVAL on FreeBSD

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/sctp.h>
#include <stdio.h>
#include <string.h>
int main(int argc,char **argv)
{
struct sockaddr_in remoteAddr;
int clientSock = socket(PF_INET,SOCK_SEQPACKET,IPPROTO_SCTP);
if(clientSock == -1) {
perror("socket");
return 1;
}
memset(&remoteAddr,0,sizeof remoteAddr);
remoteAddr.sin_family = AF_INET;
remoteAddr.sin_len = sizeof remoteAddr;
remoteAddr.sin_port = htons(5555);
remoteAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
sctp_assoc_t assoc_id = 0;
if(sctp_connectx(clientSock,(struct sockaddr*)&remoteAddr,1, &assoc_id)!= 0) {
perror("sctp_connectx");
return 1;
}
printf("Connected! Assoc ID %d\n",(int)assoc_id);
return 0;
}
When run, this code fails:
$ clang -Wall sctp_connect.c
$ ./a.out
sctp_connectx: Invalid argument
$ uname -rp
11.0-RELEASE-p9 amd64
But I cannot figure out what's wrong. The sctp_connectx() manpage says it will fail with EINVAL if an address with invalid family or no addresses was provided - but that seems not to be the case from the code.
The sctp_connectx() has several parts where it can fail with EINVAL, but truss shows it gets to the setsockopt() call, so it's the kernel that fails the call:
socket(PF_INET,SOCK_SEQPACKET,132) = 3 (0x3)
mmap(0x0,2097152,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON,-1,0x0) = 34374418432 (0x800e00000)
setsockopt(0x3,0x84,0x8007,0x800e16000,0x14) ERR#22 'Invalid argument'
I think answer is there in your query. If we follow the truss trace then as you said it fails on setsockopt().
So the error EINVAL is returned by setsockopt(). And as per FreeBSD setsockopt() manual:
[EINVAL]: Installing an accept_filter(9) on a non-listening
socket was attempted.
is the description of the error. So I think you should do below things:
Explore your socket options whether they are correct with respect to you listener socket.
Check for the errors for functions htons() and inet_addr()
And my suggestion is that you should not use inet_addr(), for more details see man pages, as per that:
Use of this function is problematic because -1 is a valid address
(255.255.255.255). Avoid its use in favor of inet_aton(),
inet_pton(3), or getaddrinfo(3), which provide a cleaner way to
indicate error return.

Linux stat(2) call gives non-existing device ID

My test program is calling stat(2) to obtain a device the file resides on.
stat.c (built with cc stat.c -o stat)
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/sysmacros.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
int main()
{
char *path = "/home/smoku/test.txt";
unsigned int maj, min;
struct stat sb;
if (stat(path, &sb) < 0) {
fprintf(stderr, "Error getting stat for '%s': %d %s\n", path, errno, strerror(errno));
return 1;
}
maj = major(sb.st_dev);
min = minor(sb.st_dev);
fprintf(stderr, "Found '%s' => %u:%u\n", path, maj, min);
return 0;
}
Got 0:44
$ ls -l /home/smoku/test.txt
-rw-r--r-- 1 smoku smoku 306 08-30 09:33 /home/smoku/test.txt
$ ./stat
Found '/home/smoku/test.txt' => 0:44
$ /usr/bin/stat -c "%d" /home/smoku/test.txt
44
But... there is no such device in my system and /home is 0:35
$ grep /home /proc/self/mountinfo
75 59 0:35 /home /home rw,relatime shared:30 - btrfs /dev/bcache0 rw,ssd,space_cache,subvolid=258,subvol=/home
Why do I get a device ID that does not exist in my system?
stat(2) in fs/stat.c uses inode->i_sb->s_dev to fill stat.st_dev
/proc/self/mountinfo in fs/proc_namespace.c uses mnt->mnt_sb->s_dev
Apparently struct inode.i_sb superblock may be different to struct vfsmount.mnt_sb superblock in case of mount of btrfs subvolume.
This is an issue inherent to btrfs implementation, which "requires non-trivial changes in the VFS layer" to fix: https://mail-archive.com/linux-btrfs#vger.kernel.org/msg57667.html

C/UNIX: port scanner

I need program that find all web servers into file that contains IP addresses.
I found that IP address is server if his port 80 is open. And I wrote this code but it doesn't work. Аlways sayd that port 80 is closed, even i write IP with open port 80. (194.153.145.104 for example). Where I wrong?
I'm cheking here for IP's with open ports: http://www.yougetsignal.com/tools/open-ports/
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
u_short port=80; /* user specified port number */
short int sock = -1; /* the socket descriptor */
struct hostent *host_info; /* host info structure */
struct sockaddr_in address; /* address structures */
char addr[1023];
char buf[20];
char *filename;
filename=argv[1];
FILE *file = fopen( filename, "r" );
while (!feof(file))
{
fscanf(file,"%s",buf);
strncpy(addr, buf, 1023);
bzero((char *)&address, sizeof(address));
address.sin_addr.s_addr = inet_addr(addr);
address.sin_port = htons(port);
address.sin_family=AF_INET;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) {
fprintf(stderr, "Error: could not assign master socket\n");
exit (1);
}
if(connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0)
printf("%s is a web server\n", addr);
else printf("%s isn't a web server\n", addr);
close(sock);
}
return 0;
}
Did you compile with warnings enabled? Using gcc I added -Wall, which says inet_addrisnt declared right. Including <arpa/inet.h> makes the program work just fine.
I suggest checking the return values of all functions and system calls you use, as to detect and locate any possible errors.
Sample output:
$ ./a.out ip.txt
127.0.0.1 is a web server
127.0.0.1 isn't a web server
EDIT: Adding some more details about my test setup, since it still does not work for the OP.
Added the include for <arpa/inet.h>
Compiled with gcc -Wall -O0 http_port_scan.c
Set up a listener on port 80 with: sudo nc -l 80
Executed: ./a.out ip.txt
The file ip.txt looks like:
~/src/so$ cat ip.txt
127.0.0.1
thuovila#glx:~/src/so$ file ip.txt
ip.txt: ASCII text
On this computer I get two lines saying "is a web server" since the nc is closed slower than my other computer. The execution environment is Ubuntu LTS 12.04 with the uname -a: Linux glx 3.2.0-43-generic #68-Ubuntu SMP Wed May 15 03:33:33 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
My suggestion is still, that you add checking for all the return values of functions and if they fail, call perror() or use some other means to figure out the error.

simple UDP server can't accept packets

I have a simple UDP server program
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char**argv)
{
int sockfd,n;
struct sockaddr_in servaddr,cliaddr;
socklen_t len;
char mesg[1000];
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(54000);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
for (;;)
{
len = sizeof(cliaddr);
n = recvfrom(sockfd,mesg,1000,0,(struct sockaddr *)&cliaddr,&len);
sendto(sockfd,mesg,n,0,(struct sockaddr *)&cliaddr,sizeof(cliaddr));
printf("-------------------------------------------------------\n");
mesg[n] = 0;
printf("Received the following:\n");
printf("%s",mesg);
printf("-------------------------------------------------------\n");
}
}
~
I compile it
gcc -m32 -o udp_server udp_server.c
and run it(./udp_server) on several linux machines, it works fine and I use a udp client client to send packets to the UDP server on these machines, they are accepted
but I have a new machine(let me call it A), it is relatively strange compared to other linux machines, as shown in https://superuser.com/questions/581442/ifconfig-command-not-found
anyway it has no "eth0" and the interfaces are:
[root#kitch proxy]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: em1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT qlen 1000
link/ether 00:1a:a0:23:86:6c brd ff:ff:ff:ff:ff:ff
First, I run the 32-bit-version of the program on it and I got
-bash: ./udp_server: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
Then, second, I compile the UDP program without '-m32' and run it on the machine A, it runs normally
Then I use UDP client to send packets to the UDP server on A
I can capture the sent packets on this machine, but the UDP server doesn't accept those packets,
are there any potential reasons for this?
maybe the binding doesn't work here because this machine is special?
thanks!
Some recent Linux distros (e.g. Fedora) have changed the name of the interface from ethX to emX, so, nothing wrong with it.
You got problem with that machine because the server is not running at all as this line claims:
-bash: ./udp_server: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
I guess you've compiled the program in a 32bit mode ( look at the -m32 gcc parameter ) in a 64bit machine with no 32bit library support installed.
Recompile it without -m32 options.

Resources