My plan is to write a user-based bandwidth control for Internet connection. To do this, I first want to write a network cable emulator.
My Linux box has three network devices:
Eth0 for normal connection to the network.
Eth1 and Eth2 are the endings of the emulated network cable.
So, what my program has to do is only to get every network packet from the input of eth1 and put it to the output ofeth2, and get every network packet from the input of eth2 and put it to the output of eth1.
With s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)), I can access every network packet. Hope that eth1and eth2 are in promiscuous mode.
But, how do I choose the network device? Later, I want to differentiate on the IP source-address that is in each packet, and how long this packet must wait, before I put it to the other network device.
With socket () you can not chose your network device. This is done with sendto () and recvfrom (). At argument 5 of recvfrom () and sendto () you put a sockaddr struct. There you have a field sockaddr.sll_ifindex where you choose your network card. In my environment 3 for enp3s0 and 4 for enp3s1.
See the cable-emulator code. I testet it 20 minutes and it worked without problems.
// network cable emulator
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
signed long long llmac (unsigned char* pmac)
{
signed long long sum= 0;
signed long long sumd;
for (signed long lauf= 0; lauf < 6; lauf++)
{
sumd= pmac[lauf];
sum= sum | (sumd << (40 - lauf*8));
}
return sum;
}
struct macliste
{
signed long long anz;
signed long long mac[100000];
macliste ();
void add (unsigned char* pmac);
signed long neu (unsigned char* pmac);
};
macliste::macliste ()
{
anz= 0;
}
void macliste::add (unsigned char* pmac)
{
if (neu (pmac))
{
mac[anz]= llmac (pmac);
anz++;
}
}
signed long macliste::neu (unsigned char* pmac)
{
signed long long smac;
smac= llmac (pmac);
for (signed long lauf= 0; lauf < anz; lauf++)
if (mac[lauf] == smac)
return 0;
return 1;
}
int main ()
{
int sockemp, socksenleft, socksenright;
signed long result;
unsigned char transferpuffer[10000];
struct sockaddr_ll sockaddrleft;
struct sockaddr_ll sockaddrright;
struct sockaddr_ll sockaddrboth;
signed long sockaddrsize= sizeof (sockaddrboth);
char ifnameleft[IFNAMSIZ] = "enp3s0";
char ifnameright[IFNAMSIZ] = "enp3s1";
struct ifreq ifindexleft;
struct ifreq ifindexright;
int indexleft;
int indexright;
// Socket öffnen
sockemp= socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
printf ("sockemp: %5d\n", sockemp);
// Socket öffnen
socksenleft= socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
printf ("socksenleft: %5d\n", socksenleft);
// Socket öffnen
socksenright= socket (AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
printf ("socksenright: %5d\n", socksenright);
printf ("\n");
/* den Index des Interfaces ermitteln */
printf ("Interfacename left: %s\n", ifnameleft);
printf ("Interfacename right: %s\n", ifnameright);
printf ("\n");
memset (&ifindexleft, 0, sizeof (struct ifreq));
memset (&ifindexright, 0, sizeof (struct ifreq));
strncpy (ifindexleft.ifr_name, ifnameleft, IFNAMSIZ);
strncpy (ifindexright.ifr_name, ifnameright, IFNAMSIZ);
result= ioctl (socksenleft, SIOCGIFINDEX, &ifindexleft);
result= ioctl (socksenright, SIOCGIFINDEX, &ifindexright);
indexleft= ifindexleft.ifr_ifindex;
indexright= ifindexright.ifr_ifindex;
printf ("interfaceresult: %5ld indexleft %5d\n", result, indexleft);
printf ("interfaceresult: %5ld indexright %5d\n", result, indexright);
printf ("\n");
/* Socketadresse vorbereiten */
memset (&sockaddrleft, 0, sizeof(struct sockaddr_ll));
sockaddrleft.sll_family = PF_PACKET; /* RAW communication */
sockaddrleft.sll_hatype = ARPHRD_ETHER; /* Ethernet */
sockaddrleft.sll_pkttype = PACKET_OTHERHOST; /* Ziel ist ein anderer Rechner */
sockaddrleft.sll_ifindex = indexleft; /* Interface-Index */
sockaddrleft.sll_halen = ETH_ALEN; /* Address length*/
/* Socketadresse vorbereiten */
memset (&sockaddrright, 0, sizeof(struct sockaddr_ll));
sockaddrright.sll_family = PF_PACKET; /* RAW communication */
sockaddrright.sll_hatype = ARPHRD_ETHER; /* Ethernet */
sockaddrright.sll_pkttype = PACKET_OTHERHOST; /* Ziel ist ein anderer Rechner */
sockaddrright.sll_ifindex = indexright; /* Interface-Index */
sockaddrright.sll_halen = ETH_ALEN; /* Address length*/
/* Socketadresse vorbereiten */
memset (&sockaddrboth, 0, sizeof(struct sockaddr_ll));
sockaddrboth.sll_family = PF_PACKET; /* RAW communication */
sockaddrboth.sll_hatype = ARPHRD_ETHER; /* Ethernet */
sockaddrboth.sll_pkttype = PACKET_OTHERHOST; /* Ziel ist ein anderer Rechner */
sockaddrboth.sll_halen = ETH_ALEN; /* Address length*/
// Schnittstellen in den Promiskuitätsmodus schalten
struct ifreq ifr;
int raw_socket;
char deviceleft[100]= "enp3s0";
char deviceright[100]= "enp3s1";
memset (&ifr, 0, sizeof (struct ifreq));
// Open A Raw Socket Deviceleft
if ((raw_socket = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
{
printf ("ERROR: Could not open socket, Got #?\n");
exit (1);
}
/* Set the device to use */
strcpy (ifr.ifr_name, deviceleft);
/* Get the current flags that the device might have */
if (ioctl (raw_socket, SIOCGIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not retrive the flags from the device.\n");
exit (1);
}
/* Set the old flags plus the IFF_PROMISC flag */
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (raw_socket, SIOCSIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not set flag IFF_PROMISC");
exit (1);
}
printf ("Entering promiscuous mode\n");
/* Configure the device */
if (ioctl (raw_socket, SIOCGIFINDEX, &ifr) < 0)
{
perror ("Error: Error getting the device index.\n");
exit (1);
}
// Open A Raw Socket Deviceright
if ((raw_socket = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
{
printf ("ERROR: Could not open socket, Got #?\n");
exit (1);
}
/* Set the device to use */
strcpy (ifr.ifr_name, deviceright);
/* Get the current flags that the device might have */
if (ioctl (raw_socket, SIOCGIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not retrive the flags from the device.\n");
exit (1);
}
/* Set the old flags plus the IFF_PROMISC flag */
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (raw_socket, SIOCSIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not set flag IFF_PROMISC");
exit (1);
}
printf ("Entering promiscuous mode\n");
/* Configure the device */
if (ioctl (raw_socket, SIOCGIFINDEX, &ifr) < 0)
{
perror ("Error: Error getting the device index.\n");
exit (1);
}
// Pakete transferieren
getchar ();
signed long packetcounter= 0;
macliste emplisteleft;
macliste emplisteright;
while (1)
{
result= recvfrom (sockemp, transferpuffer, 10000, 0, (struct sockaddr*) (&sockaddrboth), (socklen_t*)&sockaddrsize);
if ((sockaddrboth.sll_ifindex != indexleft) && (sockaddrboth.sll_ifindex != indexright))
continue;
packetcounter++;
if ((sockaddrboth.sll_ifindex == indexleft) && (emplisteright.neu (transferpuffer + 6)))
{
emplisteleft.add (transferpuffer + 6); // Ethernet II Paket Quelladresse
result= sendto (socksenright, transferpuffer, result, 0, (struct sockaddr*) &sockaddrright, sizeof (struct sockaddr_ll));
printf ("sendresult: %5ld %2d\n", result, indexright);
}
if ((sockaddrboth.sll_ifindex == indexright) && (emplisteleft.neu (transferpuffer + 6)))
{
emplisteright.add (transferpuffer + 6); // Ethernet II Paket Quelladresse
result= sendto (socksenleft, transferpuffer, result, 0, (struct sockaddr*) &sockaddrleft, sizeof (struct sockaddr_ll));
printf ("sendresult: %5ld %2d\n", result, indexleft);
}
printf ("Packet %10ld empresult: %5ld %2d %20llx %10lld %10lld\n", packetcounter, result, sockaddrboth.sll_ifindex, llmac (transferpuffer + 6), emplisteleft.anz, emplisteright.anz);
}
return 0;
}
compile with gcc 9.3.0 under ubuntu 20.04 server:
g++ transferraw.cc -std=c++17 -Wall -Wextra -Wconversion -pedantic-errors -O2 -o transferraw
Related
I have written a kernel module and userspace program such that the kernel module sends netlink multicast messages, and the userspace program reads these messages and prints them out. The kernel module and userspace program are available here (https://github.com/akshayknarayan/netlink-test) and replicated below. The code was adapted from this post: Multicast from kernel to user space via Netlink in C
If line 69 of the userspace program (the call to usleep) is commented out, then everything works; once the kernel module is loaded, it repeatedly multicasts messages and the userspace program prints them out.
However, if line 69 of the userspace program is uncommented, within a second of loading the kernel module, my VM hangs and becomes unresponsive.
Why is this the case? How can I prevent the kernel from hanging?
Linux ubuntu-xenial 4.4.0-75-generic #96-Ubuntu SMP Thu Apr 20 09:56:33 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
Userspace program:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <unistd.h>
/* Multicast group, consistent in both kernel prog and user prog. */
#define MYMGRP 22
int nl_open(void) {
int sock;
struct sockaddr_nl addr;
int group = MYMGRP;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
if (sock < 0) {
printf("sock < 0.\n");
return sock;
}
memset((void *) &addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
printf("bind < 0.\n");
return -1;
}
if (setsockopt(sock, 270, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
printf("setsockopt < 0\n");
return -1;
}
return sock;
}
void nl_recv(int sock) {
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
char buffer[65536];
int ret;
iov.iov_base = (void *) buffer;
iov.iov_len = sizeof(buffer);
msg.msg_name = (void *) &(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
printf("ret < 0.\n");
else
printf("Received message payload: %s\n", (char*) NLMSG_DATA((struct nlmsghdr *) &buffer));
}
int main(int argc, char *argv[]) {
int nls;
nls = nl_open();
if (nls < 0)
return nls;
while (1) {
nl_recv(nls);
usleep(5000);
}
return 0;
}
Kernel module:
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/gfp.h>
#include <net/sock.h>
#define MYMGRP 22
struct sock *nl_sk = NULL;
static struct timer_list timer;
void nl_send_msg(unsigned long data) {
struct sk_buff *skb_out;
struct nlmsghdr *nlh;
int res;
char *msg = "hello from kernel!\n";
int msg_size = strlen(msg);
skb_out = nlmsg_new(
NLMSG_ALIGN(msg_size), // #payload: size of the message payload
GFP_KERNEL // #flags: the type of memory to allocate.
);
if (!skb_out) {
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh = nlmsg_put(
skb_out, // #skb: socket buffer to store message in
0, // #portid: netlink PORTID of requesting application
0, // #seq: sequence number of message
NLMSG_DONE, // #type: message type
msg_size, // #payload: length of message payload
0 // #flags: message flags
);
memcpy(nlmsg_data(nlh), msg, msg_size+1);
res = nlmsg_multicast(
nl_sk, // #sk: netlink socket to spread messages to
skb_out, // #skb: netlink message as socket buffer
0, // #portid: own netlink portid to avoid sending to yourself
MYMGRP, // #group: multicast group id
GFP_KERNEL // #flags: allocation flags
);
if (res < 0) {
printk(KERN_INFO "Error while sending to user: %d\n", res);
} else {
mod_timer(&timer, jiffies + msecs_to_jiffies(1));
}
}
static int __init nl_init(void) {
struct netlink_kernel_cfg cfg = {};
printk(KERN_INFO "init NL\n");
nl_sk = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &cfg);
if (!nl_sk) {
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
init_timer(&timer);
timer.function = nl_send_msg;
timer.expires = jiffies + 1000;
timer.data = 0;
add_timer(&timer);
nl_send_msg(0);
return 0;
}
static void __exit nl_exit(void) {
printk(KERN_INFO "exit NL\n");
del_timer_sync(&timer);
netlink_kernel_release(nl_sk);
}
module_init(nl_init);
module_exit(nl_exit);
MODULE_LICENSE("GPL");
For posterity: I believe the problem was the allocation in nlmsg_new, which should not occur inside an interrupt handler (the timer handler, nl_send_msg), as explained here.
Without the sleep, I believe nlmsg_new does not need to sleep when allocating, so the requirement that an interrupt handler not sleep is not violated. However, if the userspace process lags behind the kernel, it is possible for the kernel to sleep during the allocation, causing a hang.
I have the following simple test program to create a UDP socket and bind it to a specific interface with SO_BINDTODEVICE so I can then bind() it so INADDR_ANY to recieve UDP broadcasts specifically on that interface.
//filename: bindtest.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define MY_PORT (333)
#define MY_DEVICE "enp0s3"
#define BUFFERSIZE (1000)
/* global variables */
int sock;
struct sockaddr_in sa;
struct sockaddr_in my_addr;
char buffer[BUFFERSIZE];
int main(int argc, char *argv[])
{
unsigned int echolen, clientlen;
int rc, n;
char opt_buffer[1000];
struct protoent *udp_protoent;
struct timeval receive_timeout;
int optval;
socklen_t opt_length;
sleep(1);
/* Create the UDP socket */
if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
printf ("%s: failed to create UDP socket (%s) \n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("UDP socket created\n");
/* set the recvfrom timeout value */
receive_timeout.tv_sec = 5;
receive_timeout.tv_usec = 0;
rc=setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &receive_timeout, sizeof(receive_timeout));
if (rc != 0)
{
printf ("%s: could not set SO_RCVTIMEO (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set timeout to time [s]: %d time [ms]: %d\n", receive_timeout.tv_sec, receive_timeout.tv_usec);
/* allow broadcast messages for the socket */
int true = 1;
rc=setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &true, sizeof(true));
if (rc != 0)
{
printf ("%s: could not set SO_BROADCAST (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("set SO_BROADCAST worked\n");
/* bind to a specific interface */
char device[] = MY_DEVICE;
rc=setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, device, sizeof(device));
if (rc != 0)
{
printf ("%s: could not set SO_BINDTODEVICE (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("SO_BINDTODEVICE worked\n");
/* bind my own Port */
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(MY_PORT);
rc = bind (sock, (struct sockaddr *) &my_addr, sizeof(my_addr));
if (rc < 0)
{
printf ("%s: could not bind port (%s)\n",
argv[0], strerror(errno));
exit (EXIT_FAILURE);
}
printf ("bind() worked\n");
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_BROADCAST;
sa.sin_port = htons(MY_PORT);
char data[20];
sprintf(data,"FOOBAR");
int res = sendto(sock, &data, strlen(data), 0, (struct sockaddr*)&sa, sizeof(sa));
if(res < 0){
printf("could not send\n");
} else {
printf("data sent\n");
}
close(sock);
printf ("socket closed\n");
exit(0);
}
when I run this program as a non-root user I get the following output:
$ ./bindtest
UDP socket created
set timeout to time [s]: 5 time [ms]: 0
set SO_BROADCAST worked
./bindtest: could not set SO_BINDTODEVICE (Operation not permitted)
which is pretty logical because I'm not root and SO_BINDTODEVICE is a priviledged operation. but it is included in the capability CAP_NET_RAW as I understand from this snippet of code from the Linux kernel:
static int sock_setbindtodevice(struct sock *sk, char __user *optval,
int optlen)
{
int ret = -ENOPROTOOPT;
#ifdef CONFIG_NETDEVICES
struct net *net = sock_net(sk);
char devname[IFNAMSIZ];
int index;
/* Sorry... */
ret = -EPERM;
if (!ns_capable(net->user_ns, CAP_NET_RAW))
goto out;
Well still when I do:
$ getcap bindtest
$ sudo setcap cap_net_raw+ep bindtest
$ getcap bindtest
bindtest = cap_net_raw+ep
I get the same error output:
$ ./bindtest
UDP socket created
set timeout to time [s]: 5 time [ms]: 0
set SO_BROADCAST worked
./bindtest: could not set SO_BINDTODEVICE (Operation not permitted)
And of course it works as root:
$ sudo ./bindtest
UDP socket created
set timeout to time [s]: 5 time [ms]: 0
set SO_BROADCAST worked
SO_BINDTODEVICE worked
bind() worked
data sent
socket closed
so why don't they work as expected?
The code is correct, the usage of getcap/setcap is correct, so something else must be blocking this from working.
And indeed it is because all of this was done in /home/user which on this system is mounted with the nosuid option among others.
So simply moving the binary to e.g. /usr/bin/ or any other part that is not mounted nosuid will work as expected in the first place.
(Although you also need CAP_NET_BIND_SERVICE for the bind() to work with port 333 as in the example)
My goal is to set 2 threads for serial ports: one for read, one for write.
My example is refer to the [one](//refer to how to open, read, and write from serial port in C) heavily, but I added pthread to my code:
//refer to https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c
//refer to https://stackoverflow.com/questions/6947413/how-to-open-read-and-write-from-serial-port-in-c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h> /* POSIX Threads */
#define MAX_STR_LEN 256
/*
* The values for speed are
* B115200, B230400, B9600, B19200, B38400, B57600, B1200, B2400, B4800, etc
*
* The values for parity are 0 (meaning no parity),
* PARENB|PARODD (enable parity and use odd),
* PARENB (enable parity and use even),
* PARENB|PARODD|CMSPAR (mark parity),
* and PARENB|CMSPAR (space parity).
* */
int SetInterfaceAttribs(int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0) /* save current serial port settings */
{
printf("__LINE__ = %d, error %s\n", __LINE__, strerror(errno));
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // disable break processing
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
printf("__LINE__ = %d, error %s\n", __LINE__, strerror(errno));
return -1;
}
return 0;
}/*set_interface_attribs*/
void SetBlocking(int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr(fd, &tty) != 0)
{
printf("__LINE__ = %d, error %s\n", __LINE__, strerror(errno));
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
printf("__LINE__ = %d, error %s\n", __LINE__, strerror(errno));
}/*SetBlocking*/
void *sendThread(void *parameters)
{
char sendBuff[MAX_STR_LEN];
memset(&sendBuff[0], 0, MAX_STR_LEN);
snprintf(&sendBuff[0], MAX_STR_LEN, "hello!");
int fd;
fd = *((int*)parameters);
while(1)
{
write(fd, &sendBuff[0], strlen(&sendBuff[0]) );
// sleep enough to transmit the length plus receive 25:
// approx 100 uS per char transmit
usleep((strlen(&sendBuff[0]) + 25) * 100);
}/*while*/
pthread_exit(0);
}/*sendThread */
void *readThread(void *parameters)
{
char readBuff[MAX_STR_LEN];
int fd;
fd = *((int*)parameters);
while(1)
{
ssize_t len;
memset(&readBuff[0], 0, MAX_STR_LEN);
len = read(fd, &readBuff[0], MAX_STR_LEN);
if (len == -1)
{
switch(errno)
{
case EAGAIN:
printf("__FUNCTION__ = %s, __LINE__ = %d\n", __FUNCTION__, __LINE__);
usleep(5*1000);
continue;
break;
default:
printf("__FUNCTION__ = %s, __LINE__ = %d\n", __FUNCTION__, __LINE__);
pthread_exit(0);
break;
}
}
// sleep enough to transmit the length plus receive 25:
// approx 100 uS per char transmit
usleep((len + 25) * 100);
printf("len = %d\n", (int)len);
int i;
for(i = 0; i< len; i++)
printf("%c(%d %#x)\t", readBuff[i], readBuff[i], readBuff[i]);
printf("\n");
}/*while*/
pthread_exit(0);
}/*readThread */
int main(int argc, char *argv[])
{
int fd, c, res;
struct termios oldtio,newtio;
char buf[MAX_STR_LEN];
int k;
char deviceName[MAX_STR_LEN];
memset(&deviceName[0], 0, MAX_STR_LEN);
snprintf(&deviceName[0], MAX_STR_LEN, "/dev/ttyUSB0");
k = 1;
while(argc > k)
{
if(0 == strncmp(argv[k], "-d", MAX_STR_LEN))
{
if(k + 1 < argc)
{
snprintf(&deviceName[0], MAX_STR_LEN, "%s", argv[k + 1]);
}
else
{
printf("error : -d should be follow a device!\n");
return 0;
}/*if */
}
k++;
}/*while k*/
printf("__FUNCTION__ = %s, __LINE__ = %d\n", __FUNCTION__, __LINE__);
fd = open(&deviceName[0], O_RDWR | O_NOCTTY |O_NONBLOCK| O_NDELAY);
if(0 > fd)
{
perror(&deviceName[0]);
exit(-1);
}/*if */
SetInterfaceAttribs(fd, B115200, 0); /* set speed to 115,200 bps, 8n1 (no parity)*/
SetBlocking(fd, 1);
pthread_t readThread_t, sendThread_t; /* thread variables */
pthread_create(&sendThread_t, NULL, (void *)sendThread, (void *)&fd);
pthread_create(&readThread_t, NULL, (void *)readThread, (void *)&fd);
pthread_join(sendThread_t, NULL);
pthread_join(readThread_t, NULL);
close(fd);
return 0;
}/*main*/
The send data thread works well.
But the read data thread : I could not set it as blocking, the read function returns immediately, even the read data length is zero.
How should I modify the code to make the read function be blocked?
fd = open(&deviceName[0], O_RDWR | O_NOCTTY |O_NONBLOCK| O_NDELAY);
Try removing O_NONBLOCK and O_NDELAY from your open call. Or is there a particular reason you have that even though you specifically want it to block?
If I create a socket whose type is SOCK_RAW only to send some data without receiving any data, is there any problem when kernel continue to receive network packets and copy its datagram to somebuffer (of application?). In other words, after the somebuffer is filled what will happened? error or ignore?
I don't know how to prevent kernel from delivering the copy of datagram to my application.
Reference http://sock-raw.org/papers/sock_raw 0x4 raw_input
After the IP layer processes
a new incoming IP datagram, it calls ip_local_deliver_finish() kernel function
which is responsibe for calling a registered transport protocol handler by
inspecting the protocol field of the IP header (remember from above). However
before it delivers the datagram to the handler, it checks every time if an
application has created a raw socket with the same protocol number. If there
is one or more such applications, it makes a copy of the datagram and delivers
it to them as well.
You can use shutdown(2) in order to shutdown reception part of the socket.
See shutdown man page
EDIT : I found that shutdown only works on connected (ie TCP) sockets.
With Raw socket, there are 2 possibilities :
Receive data into a temporary buffer (with recv) and discard them (perhaps in an other thread)
If I remember well, when the socket buffer is full, incoming data are automatically discarded (and data in the buffer aren't modified), so you can set the socket reception buffer size to 0 (and increase it later if needed).
Here's how to set reception buffer size to 0 :
int opt = 0;
setsockopt(sock_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
TEST
/**
* #file raw_print_pkt.c
* #brief
* #author Airead Fan <fgh1987168#gmail.com>
* #date 2012/08/22 12:35:22
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
int main(int argc, char *argv[])
{
int s;
ssize_t rn; /* receive number */
struct sockaddr_in saddr;
char packet[4096];
int count;
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
memset(packet, 0, sizeof(packet));
socklen_t *len = (socklen_t *)sizeof(saddr);
int fromlen = sizeof(saddr);
int opt = 0;
count = 0;
while(1) {
if ((rn = recvfrom(s, (char *)&packet, sizeof(packet), 0,
(struct sockaddr *)&saddr, &fromlen)) < 0)
perror("packet receive error:");
if (rn == 0) {
printf("the peer has performed an orderly shutdown\n");
break;
}
printf("[%d] rn = %lu \n", count++, rn);
if (count == 16) {
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
perror("setsocketopt failed");
} else {
fprintf(stdout, "setsocketopt successful\n");
}
// int shutdown(int sockfd, int how);
/* if (shutdown(s, SHUT_RD) < 0) {
* perror("shutdown failed");
* } */
}
}
return 0;
}
TEST 2 (same includes):
int main(int argc, char *argv[])
{
int s;
ssize_t rn; /* receive number */
char packet[4096];
int count;
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
memset(packet, 0, sizeof(packet));
int opt = 0;
count = 0;
//Set recv buffer size
if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
perror("setsocketopt failed");
} else {
fprintf(stdout, "setsocketopt successful\n");
}
//10 seconds countdown
int i = 10;
while(i > 0)
{
printf("\r%d ", i);
fflush(stdout);
i--;
sleep(1);
}
printf("\n");
while(1) {
if ((rn = recv(s, (char *)&packet, sizeof(packet), 0)) <= 0)
perror("packet receive error:");
printf("[%d] rn = %lu \n", count++, rn);
}
return 0;
}
Here's how to proceed with test 2 :
First of all, set the buffer size to 4096 (or bigger if you have a lot of traffic on your network). Compile and launch. During the 10 seconds before starting receiving data, send a lot of data to the socket. After the 10 seconds, the program will receive everything you sent during the countdown.
After that, set the buffer size to 0. Proceed as previously. After the 10 seconds, the program won't receive the data you sent during the countdown. But if you send data while it's in recvfrom, it will read them normally.
I don't really understand what you want! if you want just to inject some packets, it's simple:
#include<netinet/tcp.h> /* TCP header */
#include<netinet/ip.h> /* IP header */
/* Checksum compute function */
/* source : http://www.winpcap.org/pipermail/winpcap-users/2007-July/001984.html */
unsigned short checksum(unsigned short *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(unsigned short);
}
if(size)
cksum += *(UCHAR*)buffer;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (unsigned short)(~cksum);
}
int main (int argc, char **argv)
{
char packet_buffer[BUFFER_SIZE];
struct sockaddr_in sin;
struct iphdr *ip_header; /* IP header */
struct tcphdr *tcp_header; /* TCP header */
int flag = 1;
/* Creating RAW socket */
int raw_socket = socket (PF_INET, SOCK_RAW, IPPROTO_TCP);
ip_header = (struct iphdr *) packet_buffer;
tcp_header = (struct tcphdr *) (packet_buffer + sizeof (struct ip));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORT_NUMBER);
sin.sin_addr.s_addr = inet_addr (IP_ADDRESS);
/* Zeroing the bbuffer */
memset (packet_buffer, 0, BUFFER_SIZE);
/* Construct your IP Header */
ip_header->ihl = 5;
ip_header->version = 4;
ip_header->tos = 0;
ip_header->tot_len = sizeof (struct ip) + sizeof (struct tcphdr);
ip_header->id = htonl(CHOOSE_PACKET_ID);
ip_header->frag_off = 0;
ip_header->ttl = 255;
ip_header->protocol = 6; /* TCP. Change to 17 if you want UDP */
ip_header->check = 0;
ip_header->saddr = inet_addr (SOURCE_IP_ADDRESS_TO_SPOOF);
ip_header->daddr = sin.sin_addr.s_addr;
/* Construct your TCP Header */
tcp_header->source = htons (SOURCE);
tcp_header->dest = htons(DEST);
tcp_header->seq = random();
tcp_header->ack_seq = 0;
tcp_header->doff = 0;
tcp_header->syn = 1;
tcp_header->window = htonl(65535);
tcp_header->check = 0;
tcp_header->urg_ptr = 0;
/* IP Checksum */
ip_header->check = checksum((unsigned short *) packet_buffer, ip_header->tot_len >> 1);
if (setsockopt(raw_socket, IPPROTO_IP, IP_HDRINCL, &flag, sizeof(flag)) < 0)
{
/* ERROR handling */
}
while (1)
{
/* Send the packet */
if (sendto(raw_socket, packet_buffer, ip_header->tot_len, 0, (struct sockaddr *) &sin, sizeof (sin)) < 0)
{
/* ERROR handling */
}
/* The rest of your need */
}
return 0;
}
Is it possible to use ICMP sockets under the IP protocol? Maybe something like:
socket(PF_INET, <type>, IPPROTO_ICMP)?
What should I put in the <type> field? I saw some examples using SOCK_RAW, but won't that prevent the OS from doing his job handling the IP protocol?
And another thing. How can the OS know to which process he should send the ICMP datagrams, since there are no ports involved with the protocol?
Linux have a special ICMP socket type you can use with:
socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP);
This allows you to only send ICMP echo requests The kernel will handle it specially (match request/responses, fill in the checksum).
This only works if a special sysctl is set. By default not even root can use this kind of socket. You specify the user groups that can access it. To allow root (group 0) to use ICMP sockets, do:
sysctl -w net.ipv4.ping_group_range="0 0"
Here is an example program to demonstrate the very basic usage of sending an ICMP echo request:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/select.h>
//note, to allow root to use icmp sockets, run:
//sysctl -w net.ipv4.ping_group_range="0 0"
void ping_it(struct in_addr *dst)
{
struct icmphdr icmp_hdr;
struct sockaddr_in addr;
int sequence = 0;
int sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP);
if (sock < 0) {
perror("socket");
return ;
}
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_addr = *dst;
memset(&icmp_hdr, 0, sizeof icmp_hdr);
icmp_hdr.type = ICMP_ECHO;
icmp_hdr.un.echo.id = 1234;//arbitrary id
for (;;) {
unsigned char data[2048];
int rc;
struct timeval timeout = {3, 0}; //wait max 3 seconds for a reply
fd_set read_set;
socklen_t slen;
struct icmphdr rcv_hdr;
icmp_hdr.un.echo.sequence = sequence++;
memcpy(data, &icmp_hdr, sizeof icmp_hdr);
memcpy(data + sizeof icmp_hdr, "hello", 5); //icmp payload
rc = sendto(sock, data, sizeof icmp_hdr + 5,
0, (struct sockaddr*)&addr, sizeof addr);
if (rc <= 0) {
perror("Sendto");
break;
}
puts("Sent ICMP\n");
memset(&read_set, 0, sizeof read_set);
FD_SET(sock, &read_set);
//wait for a reply with a timeout
rc = select(sock + 1, &read_set, NULL, NULL, &timeout);
if (rc == 0) {
puts("Got no reply\n");
continue;
} else if (rc < 0) {
perror("Select");
break;
}
//we don't care about the sender address in this example..
slen = 0;
rc = recvfrom(sock, data, sizeof data, 0, NULL, &slen);
if (rc <= 0) {
perror("recvfrom");
break;
} else if (rc < sizeof rcv_hdr) {
printf("Error, got short ICMP packet, %d bytes\n", rc);
break;
}
memcpy(&rcv_hdr, data, sizeof rcv_hdr);
if (rcv_hdr.type == ICMP_ECHOREPLY) {
printf("ICMP Reply, id=0x%x, sequence = 0x%x\n",
icmp_hdr.un.echo.id, icmp_hdr.un.echo.sequence);
} else {
printf("Got ICMP packet with type 0x%x ?!?\n", rcv_hdr.type);
}
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("usage: %s destination_ip\n", argv[0]);
return 1;
}
struct in_addr dst;
if (inet_aton(argv[1], &dst) == 0) {
perror("inet_aton");
printf("%s isn't a valid IP address\n", argv[1]);
return 1;
}
ping_it(&dst);
return 0;
}
Note that the kernel will reject and fail the sendto() call if the data sent does not have room for a proper ICMP header, and the ICMP type must be 8 (ICMP_ECHO) and the ICMP code must be 0.
Yes it is possible, since the ping command does ICMP.
To find out the syscalls involved, you can strace that command (under root).
You could also glance into that command's source code, e.g. Debian's ping
And there is the liboping library to help you...