get notified on connection state change with IWD - linux

I wrote a systray area applet that displays the status of IWD backed WiFi connection. Right now my app polls iwctl station wlp4s0 show every minute and parses the result. Is there a way to avoid polling and use some sort of a notification hook?

I have found a way to do this with linux netlink protocol.
man 7 netlink
apt get install libnl-3-dev libnl-genl-3-dev
Basically:
int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
struct sockaddr_nl snl = { .... };
bind(fd, &snl, sizeof(snl));
while (1) {
recv(fd, buf, sizeof(buf), 0);
struct nlmsghdr *nlh = (struct nlmsghdr *)buf;
...
}
recv will block until events related to network interfaces happen, then data you need is in nlh.
For example check for new connection:
if (nlh->nlmsg_type == RTM_NEWADDR) ...

Related

Linux application for reading packets from raw socket stops receiving packets after sometime

I have written a Linux application program that receives UDP packets transmitted from a Desktop with fixed & known IP-address on the network. I am using a raw socket to receive packets on my system and filter the received packets based on the source address.
The problem I am facing is, the program runs fine for some time and I get all the required packets, but after a couple of hours, the application stops getting any packets. If I run the command,
tcpdump -i eth0 src 192.168.20.48 on my system, then I see that the system continues to receive the expected packets. But I am not sure what is causing my program to stop receiving packets.
Below is the code snippet used to open a raw socket, receive packets, and filter out the UDP packets transmitted from the known IP address.
int main()
{
int sockfd;
int one = 1;
struct timeval tv;
socklen_t len;
int bytes;
unsigned char tsptr[2048];
struct sockaddr_in cliaddr;
struct iphdr *iph;
int result=0;
char source_add[50];
char expected_source_add[50];
len = sizeof(struct sockaddr_in);
// Creating socket file descriptor
if ((sockfd = socket(AF_INET , SOCK_RAW , IPPROTO_UDP)) < 0 ) {
BRH_PERROR("socket creation failed");
return 1;
}
tv.tv_sec = 30;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &one, sizeof(one));
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,(char*)&tv,sizeof(tv));
strcpy(expected_source_add, "192.168.20.48");
while (1) {
/*Read fixed data count from socket*/
bytes =recvfrom(sockfd, tsptr, 1500, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);
iph=(struct iphdr*)tsptr;
//get only UDP packet
if (iph->protocol != 17) continue;
strcpy(source_add,inet_ntoa(cliaddr.sin_addr));
result = strcmp(expected_source_add,source_add);
/*receive data from expected IP address only*/
if( result == 0) {
//Consume the packet
}
}
return 0;
}
Any clue on why the packet receive stops on my application, even though tcpdump shows that packets are being received on the interface, will be helpful.
The code you write here can not see any problem that you describe, I think you should do something like below.
1. using wireshark or tcpdump to see if the nic receive packets successfully
2. beyond the program, do you use any buffer or message queue and are they working good?
3. using tools to see if there exists any memeory leak
4. writing log in every step, especially around recvefrom and strcmp

redirect ethernet packets through sk_buff to localhost

I am writing a Linux kernel module which redirects a packet to the localhost webserver ,which was originally forwarded through this machine using bridge . It also redirects to reply to the client . The client is oblivious of the redirection . So there are 2 parts
1. all forwarded packets through bridge to some webserver outside are redirected to local webserver .
The output of the localhost webserver is channelled to the original client
I am able to do the second part through nf_hook NF_INET_LOCAL_OUT
unsigned int snoop_hook_reply( unsigned int hooknum, struct sk_buff *skb,
const struct net_device *in, const struct net_device *out,
int(*okfn)( struct sk_buff * ) )
{
int offset, len;
struct ethhdr *ethh;
struct iphdr *iph;
struct tcphdr *tcph;
bool flag = false;
struct net_device *eth1_dev , *lo_dev;
if (!skb) return NF_ACCEPT;
iph = ip_hdr(skb);
if (!iph) return NF_ACCEPT;
skb_set_transport_header(skb, iph->ihl * 4);
tcph = tcp_hdr(skb);
/* skip lo packets */
if (iph->saddr == iph->daddr) return NF_ACCEPT;
if (tcph->dest == htons(80))
flag=true;
if(flag != true)
return NF_ACCEPT;
// correct the IP checksum
iph->check = 0;
ip_send_check (iph);
//correct the TCP checksum
offset = skb_transport_offset(skb);
len = skb->len - offset;
tcph->check = 0;
if(skb->len > 60){
tcph->check = csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, csum_partial((unsigned char *)tcph,len,0));
}
else{
tcph->check = ~csum_tcpudp_magic((iph->saddr), (iph->daddr), len, IPPROTO_TCP, 0);
}
//send to dev
eth1_dev = dev_get_by_name(&init_net,"eth1");
lo_dev = dev_get_by_name(&init_net,"lo");
skb->dev = eth1_dev;
ethh = (struct ethhdr *) skb_push(skb, ETH_HLEN);
skb_reset_mac_header(skb);
skb->protocol = ethh->h_proto = htons(ETH_P_IP);
memcpy (ethh->h_source,eth1_dev->dev_addr , ETH_ALEN);
memcpy (ethh->h_dest, d_mac, ETH_ALEN); // d_mac is mac of the gateway
dev_queue_xmit(skb);
return NF_STOLEN;
}
the above code works perfectly for me . One issue is that later on I will mangle the packet so need to create a new sk_buff, probably .
I am not able to do the 1st part through NF_INET_PRE_ROUTING, I am not able to push the packet/sk_buff to the webserver process through the TCP/IP stack. I tried using dev_queue_xmit() function with skb->dev as both eth1 and lo . I am seeing the packets hitting on the lo or eth1 through tcpdump . But the packets are not reaching the localhost webserver. Can anyone help me regarding this or point to some similar answered question . I believe instead of dev_queue_xmit() I need to call some receiving function . Also when packets arrive in NF_INET_PREROUTING, I the ethernet headers are already there so I am not forming it .
I have already accomplished the above tasks in variety of ways , first using raw sockets , then using nf_queue , now I want to see the performance through this method.
Thanks
If you want to receive the packet locally, you cannot call dev_queue_xmit() on eth1 as it will be sent out. You probably need to call netif_rx() after pointing the skb->dev to eth1/lo.
One more point is if the dest-ip is not your local host ip, then you need to avoid routing again otherwise, there will be no use of your interception.
To achieve this, either you need to modify packet's dest ip to eth1/lo IP or
fool the IP layer by using skb_dst_set() to set "rth->dst.input= ip_local_deliver" for packet to be accepted as local packet.

how to use SIOCIFDESTROY in FreeBSD?

My app creates a tap interface, and everything works well. But on FreeBSD, when it exits, the tap interface remains. To delete it, I have to manually run this command:
sudo ifconfig tap0 destroy
But I'd like to do this programmatically within my application. Where can I find the docs for SIOCIFDESTROY? Here is what I've tried when my app exits:
struct ifreq ifr;
memset(&ifr, '\0', sizeof(ifr));
strcpy(ifr.ifr_name, "tap0");
int sock = socket(PF_INET, SOCK_STREAM, 0);
err = ioctl(sock, SIOCIFDESTROY, &ifr);
At this point, err is zero, but the tap interface still exists when the app ends. Anyone know what else I might be missing?
The tricky part was trying to find documentation to describe is the parameter to pass to ioctl(). I never did find anything decent to read.
Turns out a completely blank ifreq with just the tap interface name set is all that is needed. In addition to the original code I included in the question, also note that I close the tap device file descriptor prior to deleting the actual tap interface. I can only imagine that might also be relevant:
close(device_fd);
struct ifreq ifr;
memset(&ifr, '\0', sizeof(ifr));
strcpy(ifr.ifr_name, "tap0");
int sock = socket(PF_INET, SOCK_STREAM, 0);
err = ioctl(sock, SIOCIFDESTROY, &ifr);

Develop simple net_device in the linux kernel

I am trying to implement a netdevice (net_device) in linux kernel. This is simple net_device which pass the command/data from user space to kernel space and vice versa this is the goal of this simple net_device. I am using socket for passing command/data from user space to kernel space . After googling i successed in registering net_device and able to see my device in /sys/class/net/abc0 (device name)
when coming to file operation there is no clear idea of the flow
struct net_device_ops
{
.ndo_open =open,
.ndo_close = close,
.ndo_start_xmit = start_xmit
}
if i issue write in socket will it call start_xmit in data link layer.
If i want to call open method, how to call it using socket
How to call start_xmit using socket
How will i find , there is data packet in the receive buffer and pass it to user space.
There is no clear flow/information about simple net_device (except ethernet) can any suggest a link/pdf.
I tried writing simple socket program to test open,close,start_xmit. where socket read/write is not calling open,close,star_xmit .
Is there any way to test the developed net_device ?
Thank you
I found how to test the open,close function .
type : ifconfig abc0(Device name) up will call open method
type : ifconfig abc0(Device name) down will call close method
Can some one help me how to test these methods with sockets.
SIOCSIFFLAGS, -> IFF_UP you can actually set or unset it while doing an ioctl to the netdevice abc0.
first off you have to create a dgram socket,
then use ifreq structure defined in net/if.h
and fill interface and iff_flags
iff_flags can be set with IFF_UP or can be negated with the same IFF_UP to make interface down
and then close the socket.
#include <net/if.h>
....
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
goto fail;
}
struct ifreq ifreq;
strcpy(ifreq.ifr_name, "abcd0");
ifreq.iff_flags |= IFF_UP;
if (ioctl(sock, &ifreq, sizeof(ifreq) < 0) {
perror("ioctl setting interface up");
}
ifreq.iff_flags ~= IFF_UP;
if (ioctl(sock, &ifreq, sizeof(ifreq) < 0) {
perror("ioctl setting interface down");
}
close(sock);
offtopic:
will you please share your code? we can understand too about your network device :)

Identify program that connects to a Unix Domain Socket

I have a program that is listening to a Unix Domain Socket.
When a client connects to the socket I'd like to find out which program connected and then decide if I allow the connection or not (based on the user/group settings).
Is this possible under Linux, and if so, how?
Yes, this is possible on Linux, but it won't be very portable. It's achieved using what is called "ancillary data" with sendmsg / recvmsg.
Use SO_PASSCRED with setsockopt
Use SCM_CREDENTIALS and the struct ucred structure
This structure is defined in Linux:
struct ucred {
pid_t pid; /* process ID of the sending process */
uid_t uid; /* user ID of the sending process */
gid_t gid; /* group ID of the sending process */
};
Note you have to fill these in your msghdr.control, and the kernel will check if they're correct.
The main portability hindrance is that this structure differs on other Unixes - for example on FreeBSD it's:
struct cmsgcred {
pid_t cmcred_pid; /* PID of sending process */
uid_t cmcred_uid; /* real UID of sending process */
uid_t cmcred_euid; /* effective UID of sending process */
gid_t cmcred_gid; /* real GID of sending process */
short cmcred_ngroups; /* number or groups */
gid_t cmcred_groups[CMGROUP_MAX]; /* groups */
};
I searched for this quite a bit, so I will show you this example on how to use SO_PEERCRED on a socket sock to get the pid/uid/gid of the peer of a socket:
int len;
struct ucred ucred;
len = sizeof(struct ucred);
if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) {
//getsockopt failed
}
printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n",
(long) ucred.pid, (long) ucred.uid, (long) ucred.gid);
Perhaps getpeername or getsockname could help. and I think that the permission of your unix socket are useful (not sure of that). And you might read the link inside /proc/self/fd/12 if your accept-ed socket is 12.
EDIT
using ancillary data for credentials and sendmsg is much better, as suggested by cnicutar below.

Resources