An often overlooked function that requires no external library, but basically has no documentation whatsoever.
UPDATE (2010-10-11): The linux man-pages now have documentation of the getaddrinfo_a, you can find it here: http://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo_a.3.html
As a disclaimer I should add that I'm quite new to C but not exactly a newbie, so there might be bugs, or bad coding practices, please do correct me (and my grammar sucks too).
I personally didn't know about it until I came upon this post by Adam Langley, I shall give a few code snippets to illustrate the usage of it and clarify some things that might not be that clear on first use. The benefits of using this is that you get back data readily usable in socket(), listen() and other functions, and if done right you won't have to worry about ipv4/v6 either.
So to start off with the basics, as taken from the link above (you will need to link against libanl (-lanl)) :
Here is the function prototype:
int getaddrinfo_a(int mode, struct gaicb *list[], int ent,
struct sigevent *);
The mode is either GAI_WAIT (which is probably not what you want) and GAI_NOWAIT for async lookups
The gaicb argument accepts an array of hosts to lookup with the ent argument specifying how many elements the array has
The sigevent will be responsible for telling the function how we are to be notified, more on this in a moment
A gaicb struct looks like this:
struct gaicb {
const char *ar_name;
const char *ar_service;
const struct addrinfo *ar_request;
struct addrinfo *ar_result;
};
If you're familiar with getaddrinfo, then these fields correspond to them like so:
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
The node is the ar_name field, service is the port, the hints argument corresponds to the ar_request member and the result is stored in the rest.
Now you specify how you want to be notified through the sigevent structure:
struct sigevent {
sigval_t sigev_value;
int sigev_signo;
int sigev_notify;
void (*sigev_notify_function) (sigval_t);
pthread_addr_t *sigev_notify_attributes;
};
You can ignore the notification via setting _sigev_notify_ to SIGEV_NONE
You can trigger a signal via setting sigev_notify to SIGEV_SIGNAL and sigev_signo to the desired signal. Note that when using a real-time signal (SIGRTMIN-SIGRTMAX, always use it via the macros and addition SIGRTMIN+2 etc.) you can pass along a pointer or value in the sigev_value.sival_ptr or sigev_value.sival_int member respectivley
You can request a callback in a new thread via setting sigev_notify to SIGEV_NONE
So basically if you want to look up a hostname you set ar_name to the host and set everything else to NULL, if you want to connect to a host you set ar_name and ar_service , and if you want to create a server you specify ar_service and the ar_result field. You can of course customize the ar_request member to your hearts content, look at man getaddrinfo for more info.
If you have an event loop with select/poll/epoll/kqueue you might want to use signalfd for convenience. Signalfd creates a file descriptor on which you can use the usuall event polling mechanisms like so:
#define _GNU_SOURCE //yes this will not be so standardish
#include <netdb.h>
#include <signal.h>
#include <sys/signalfd.h>
void signalfd_setup(void) {
int sfd;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGRTMIN);
sigprocmask(SIG_BLOCK, &mask, NULL); //we block the signal
sfd = signalfd(-1, &mask, 0);
//add it to the event queue
}
void signalfd_read(int fd) {
ssize_t s;
struct signalfd_siginfo fdsi;
struct gaicb *host;
while((s = read(fd, &fdsi, sizeof(struct signalfd_siginfo))) > 0){
if (s != sizeof(struct signalfd_siginfo)) return; //thats bad
host = fdsi.ssi_ptr; //the pointer passed to the sigevent structure
//the result is in the host->ar_result member
create_server(host);
}
}
void create_server(struct gaicb *host) {
struct addrinfo *rp, *result;
int fd;
result = host->ar_result;
for(rp = result; rp != NULL; rp = rp->ai_next) {
fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
bind(fd, rp->ai_addr, rp->ai_addrlen);
listen(fd, SOMAXCONN);
//error checks are missing!
freeaddrinfo(host->ar_request);
freeaddrinfo(result);
//you should free everything you put into the gaicb
}
}
int main(int argc, char *argv[]) {
struct gaicb *host;
struct addrinfo *hints;
struct sigevent sig;
host = calloc(1, sizeof(struct gaicb));
hints = calloc(1, sizeof(struct addrinfo));
hints->ai_family = AF_UNSPEC; //we dont care if its v4 or v6
hints->ai_socktype = SOCK_STREAM;
hints->ai_flags = AI_PASSIVE;
//every other field is NULL-d by calloc
host->ar_service = "8888"; //the port we will listen on
host->ar_request = hints;
sig.sigev_notify = SIGEV_SIGNAL;
sig.sigev_value.sival_ptr = host;
sig.sigev_signo = SIGRTMIN;
getaddrinfo_a(GAI_NOWAIT, &host, 1, &sig);
signalfd_setup();
//start your event loop
return 0;
}
You can of course use a simple signal handler for this job too, look at man sigaction for more info.
Related
First of all, i understand what the code (see at the end of the post) does in general and i dont want an overall explanation.
What i don't understand is this particular line of code:
memset(&hints, 0, sizeof(struct addrinfo));
What i get so far is that a memset() is a function to fill the string which it is pointed to up.
It has three parameters, first the pointer to the string, second the value to be set and third the number of bytes set to the value.
In this case the the value to be filled up is &hints which would be the adress of the variable hints. The value which should be set is 0 so it is filled with zeroes. And last it is filled to the size of the struct addrinfo.
So in this case memset() generates for the variable hints zeroes to the size of the struct. Did i get this right?
If yes, why is this needed in my example?
#include <stdlib.h>/* EXIT_SUCCESS */
#include <stdio.h>/* printf */
#include <string.h>/* memset() */
#include <errno.h>/* int errno */
#include <sys/types.h>/* socket defines */
#include <sys/socket.h>/* socket() */
#include <netdb.h>/* getaddrinfo() */
#define ECHO_PORT "7"
int main (int argc, char* argv[]){
if (argc != 2) {
printf ("Usage: %s HOSTNAME\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Resolve host addresses: */
struct addrinfo hints;
struct addrinfo* result, *rp;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;/* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;/* Stream socket */
hints.ai_flags = 0;
hints.ai_protocol = 0;/* Any protocol */
int err = getaddrinfo(argv[1], ECHO_PORT, &hints, &result);
/* Handle potential error: */
if (err) {
printf("Error: getaddrinfo: %s\n", gai_strerror(err));
exit(EXIT_FAILURE);
}
/* Print names found: */
printf("Addresses for %s:\n", argv[1]);
for (rp = result; rp != NULL; rp = rp->ai_next) {
int af = rp->ai_family;
char* address = NULL;
int ok;
if (AF_INET == rp->ai_family) {
uint8_t in_addr =((struct sockaddr_in*)rp->ai_addr)->sin_addr.s_addr;
address = malloc(INET_ADDRSTRLEN);
ok = inet_ntop(af, &in_addr, address, INET_ADDRSTRLEN);
}
if (AF_INET6 == rp->ai_family) {
char* in6_addr =((struct sockaddr_in6*)rp->ai_addr)->sin6_addr.s6_addr;
address = malloc(INET6_ADDRSTRLEN);
ok = inet_ntop(af, in6_addr, address, INET6_ADDRSTRLEN);
}
if (ok) {
printf("%s\n", address);
}
else {
perror("inet_ntop");
}
free(address);
}
freeaddrinfo(result);
return EXIT_SUCCESS;
}
Yes, you understood it correctly.
It's needed in the code below because in
struct addrinfo hints;
hints is left uninitialized and the programmer wanted to make sure all the fields a zeroed.
An easier solution would be to initialize it directly:
addrinfo hints{}; // C++11 and later
struct addrinfo hints = {0}; /* C and C++ */
and skip memset.
Another option is to initialize it with the correct values using designated initializers (C99 and C++20). In C you can specify the fields out-of-order, but not in C++, so this order would work in both:
struct addrinfo hints = { /* "struct" not needed in C++ */
.ai_flags = 0,
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
/* .ai_protocol and the rest will be zeroed */
};
For older standards without designated initializers:
struct addrinfo hints = {
0,
AF_UNSPEC,
SOCK_STREAM
};
Yes, your understanding is basically correct. The code is simply filling the entire hints variable with 0x00 bytes before passing it to getaddrinfo(). This is needed to initialize the hints to a default state, which is important because addrinfo contains flags and memory pointers to control getaddrinfo()'s behavior. So you can't just leave the hints in an uninitialized state, it will contain random garbage that will cause undefined behavior, confusing getaddrinfo() and/or even leading to corrupted memory, crashes, etc.
Using the memset() is a quick way to initialize all of the fields of the hints to zeros in one quick operation, instead of initializing each field individually. This way, you can focus on assigning values to just the fields you are actually interested in.
An easier way to initialize the hints is like this instead:
struct addrinfo hints = {0};
This will value-initialize the first field (ai_flags) to 0, and default-initialize the remaining fields to their default values, which in this case is also zeros.
You're correct that memset is being used here to set all bytes of hints to zero.
This is being done so that any field that is not explicitly set later has the value 0. Because hints is uninitialized, its fields have indeterminate values, so doing this sets all fields to 0. If you look at the definition of struct addrinfo:
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
You can see that the program only explicitly sets the first 4 fields. The call to memset first takes care of the rest.
I am working with some code I found and I am trying to understand the for loop logic in this.
It seems like the for loop is not being used and it is always going to break. So I am not understanding what sort of information is returned from getaddrinfo() that would cause this to loop over again and check for something?
Question: Basically what is this for loop trying to accomplish? It seems to me it is just checking that list is not NULL because no loop is occurring, ever.
tcp_ctx* tcp_new_ctx(INT family, CHAR *host, CHAR *port) {
struct addrinfo *list = NULL;
struct addrinfo *e = NULL;
struct addrinfo hints;
tcp_ctx *c = NULL;
WSADATA wsa;
INT on = 1;
WSAStartup(MAKEWORD(2, 0), &wsa);
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// try to resolve network address for host
//list contains linked list of addrinfo structs containing information on the host
if (getaddrinfo(host, port, &hints, &list) != 0) {
return NULL;
}
c = tcp_alloc(sizeof(tcp_ctx));
// Traverse linked list from getaddr info
for (e = list; e != NULL; e = e->ai_next) {
if (family == AF_INET) {
memcpy(&c->v4, e->ai_addr, e->ai_addrlen);
c->ai_addr = (SOCKADDR*)&c->v4;
} else if (family == AF_INET6) {
memcpy(&c->v6, e->ai_addr, e->ai_addrlen);
c->ai_addr = (SOCKADDR*)&c->v6;
} else {
return NULL;
}
c->ai_addrlen = e->ai_addrlen;
// create socket and event for signalling
c->s = socket(family, SOCK_STREAM, IPPROTO_TCP);
if (c->s != SOCKET_ERROR) {
// ensure we can reuse same port later
setsockopt(c->s, SOL_SOCKET, SO_REUSEADDR, (CHAR*)&on, sizeof(on));
}
break;
}
freeaddrinfo(list);
return c;
}
What would be in the list?
The following quote is from the man page of getaddrinfo of glibc, but AFAIK it applies equally to the Windows version:
There are several reasons why the linked list may have more than one
addrinfo structure, including: the network host is multihomed, accessible over multiple protocols (e.g., both AF_INET and AF_INET6); or
the same service is available from multiple socket types (one
SOCK_STREAM address and another SOCK_DGRAM address, for example).
So, basically the host could have multiple ways to contact it, and getaddrinfo is listing all (known) ways. Note that the same man page further states:
Normally, the application should try using the addresses in the order
in which they are returned. The sorting function used within getaddrinfo() is defined in RFC 3484;
Of course, here too we don't know whether this also holds for the Windows implementation(s), but if it's specified in an RFC then that might be the case.
What is this for loop trying to accomplish?
As it stands you could remove the loop. I think there might be a reason to keep (= fix) it, though: Consider the case where the first returned entry has a family of neither AF_INET nor AF_INET6. The code in your questions then just fails (return NULL;), but there could be a later entry in the list which could have that family.
I am trying to implement a server / multi client program in Linux with C using select() and fd_sets. I am trying to broadcast messages sent from one connected client to all other connected clients but I don't know how to access the sockets for other clients in the fd_set once they are added dynamically. I am trying to replicate an implementation of this I found in C++ but the fd_set in C doesn't have the properties as C++. This is the code I'm trying to replicate:
for(int i = 0; i < master.fd_count; i++)
{
SOCKET outSock = master.fd_array[i];
if(outSock != listening && outSock != sock)
{
send(outSock, buffer, 250);
}
}
where master is the fd_set, listening is the original socket listening for new clients and sock is the socket the message about to be broadcast came from.
Can any one help me learn how to access the fd_set socket elements to be able to do != comparisons on them like in the example. Or alternatively, point me to another method to implement the multi client setup where I can broadcast a message back to all connected clients. I initially tried using multi processes with fork() pipes but I could not find enough information on how to implement that properly.
In C, you use the macro FD_ISSET to find out whether a given bit is set or not. See the manual page for select(2) for details.
The basic idea is that first you zero the set with FD_ZERO, then you set some bits with FD_SET, then you call select() (or pselect(), according to taste). When select() returns you iterate over the set and use FD_ISSET to find out whether you can do a non-blocking I/O operation on the specified descriptor.
There are many examples on the net; for example, an example from IBM.
Get in the habit of reading manpages. The one for select(2) lists the macros provided for fd_set:
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);
You can use FD_SET and FD_ISSET macros to set or test the bits in fd_set that correspond to your filedescriptors.
Keep your own list of connected "users", like a linked list of structures containing what is needed for each user (like username and other data) and the socket descriptor.
Then if you need to send a message to all users just iterate over this list. Since the structures contain all information about the user, it's easy to skip one or more users when iterating, for example to not send to the user from which the message originated.
Simple example
struct user
{
char *name; // Name of the user
SOCKET socket; // Socket descriptor for communication
// Other data needed for the user...
struct user *next; // For linking into a list
};
// The list of all users
struct user *users = NULL;
// Broadcast a message to *all* connected users
void broadcast(const char *message)
{
for (struct user *u = users; u != NULL; u = u->next)
{
send(u->socket, message, strlen(message), 0);
}
}
// Broadcast to all except a specific user
void broadcast_except_name(const char *message, const char *name)
{
for (struct user *u = users; u != NULL; u = u->next)
{
if (strcmp(u->name, name) != 0)
{
send(u->socket, message, strlen(message), 0);
}
}
}
// Broadcast to all except a specific socket
void broadcast_except_socket(const char *message, SOCKET socket)
{
for (struct user *u = users; u != NULL; u = u->next)
{
if (u->socket != socket)
{
send(u->socket, message, strlen(message), 0);
}
}
}
[Functions for creating or otherwise operating on the list omitted]
I want to echo a packet in kernel space. I run an echo server on this machine with port 6000.
Now a client runs on another machine sending data to the echo server. Now, what I want to do is to echo the packet back from the kernel space. I do not want to bother the server with the packet, and it will be echoed silently from the kernel space. I am presenting my codes below:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netdevice.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/mm.h>
#include <linux/err.h>
#include <linux/crypto.h>
#include <linux/init.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/route.h>
#include <net/checksum.h>
#include <linux/netfilter_ipv4.h>
#define IP_HDR_LEN 20
#define UDP_HDR_LEN 8
#define TOT_HDR_LEN 28
static unsigned int pkt_echo_begin(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *));
static struct nf_hook_ops pkt_echo_ops __read_mostly = {
.pf = NFPROTO_IPV4,
.priority = 1,
.hooknum = NF_INET_PRE_ROUTING,
.hook = pkt_echo_begin,
};
static int __init pkt_echo_init(void)
{
printk(KERN_ALERT "\npkt_echo module started ...");
return nf_register_hook(&pkt_echo_ops);
}
static void __exit pkt_echo_exit(void)
{
nf_unregister_hook(&pkt_echo_ops);
printk(KERN_ALERT "pkt_echo module stopped ...");
}
static unsigned int pkt_echo_begin (unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct udphdr *udph;
unsigned char *data;
unsigned char *temp;
__u16 dst_port, src_port;
int data_len;
if (skb) {
iph = (struct iphdr *) skb_header_pointer (skb, 0, 0, NULL);
if (iph && iph->protocol &&(iph->protocol == IPPROTO_UDP)) {
udph = (struct udphdr *) skb_header_pointer (skb, IP_HDR_LEN, 0, NULL);
src_port = ntohs (udph->source);
dst_port = ntohs (udph->dest);
if (dst_port == 6000) {
printk(KERN_ALERT "\nUDP packet goes in");
data = (unsigned char *) skb_header_pointer (skb, TOT_HDR_LEN, 0, NULL);
data_len = skb->len - TOT_HDR_LEN;
struct sk_buff *newskb;
struct iphdr *newiph;
struct udphdr *newudph;
unsigned char *newdata;
unsigned int newdata_len;
newskb = skb_copy(skb, GFP_ATOMIC);
newiph = (struct iphdr *) skb_header_pointer (newskb, 0, 0, NULL);
newudph = (struct udphdr *) skb_header_pointer (newskb, IP_HDR_LEN, 0, NULL);
newdata = (unsigned char *) skb_header_pointer (newskb, TOT_HDR_LEN, 0, NULL);
newiph->saddr = iph->daddr;
newiph->daddr = iph->saddr;
newudph->source = udph->dest;
newudph->dest = udph->source;
struct sk_buff *tempskb;
tempskb = skb_copy(skb, GFP_ATOMIC);
*tempskb = *skb;
*skb = *newskb;
*newskb = *tempskb;
kfree_skb(newskb);
}
}
}
return NF_ACCEPT;
}
module_init(pkt_echo_init);
module_exit(pkt_echo_exit);
MODULE_AUTHOR("Rifat Rahman Ovi: <rifatrahmanovi#gmail.com>");
MODULE_DESCRIPTION("Echoing a packet from kernel space.");
MODULE_LICENSE("GPL");
Now I do not understand what is needed. I captured the packet in the PRE_ROUTING hook.
Then I created a new skb, populated it from the old received skb, and then altered the
source address (saddr), destination address (daddr), source port (source), and destination
port (dest), so that the packet can be forwarded from the PRE_ROUTING hook. As routing
decisions are made after the packet passes from the hook, I look for the packet to be forwarded to its originating source. But for some reason it is not doing like that. The packet is received, and everything is altered, but the packet does not seem to move backwards. I do not understand what is missing, and also what is needed to make it work. More specifically, what is needed to send a packet to the network from a PRE_ROUTING hook?
A lot is missing.
First of all, the netfilter hook you used is PRE-ROUTING which captures INCOMING packets and so unless you use some kernel function to transmit the packet you've built, return NF_ACCEPT will only let the packet you altered(or didn't) continue on its way (which is TO the local system, not from it).
Read about functions like dev_queue_xmit(struct sk_buff *) but notice that before using this function, your SKB has to have the Link-layer header because this function actually queues your packet in a queue to be sent right away to the NIC and it's your job to set the Link layer addresses.
Second, remember that after you alter the IP-header addresses you have to re-calculate the checksum of the packet or else your packet will be discarded at the other end.
Third, notice that doing what you're trying to do in kernel space is largely considered a VERY bad practice.
Kernel modules exist for a reason and this is not one of them, netfilter is a great tool, but it's not really supposed to be used for sending normal traffic.
EDIT:
Reading your latest comment; I'd suggest you read about libPCap library, it should serve you very well and still keep your work in its right place, the user-space.
In addition to the previous answer, another technique that can be used to echo an UDP packet from the netfilter callback is:
send the packet back, as a new UDP packet, using:
int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
in net/socket.c
or also using the netpoll library, as said in the answer here.
The original packet can than be dropped using NF_DROP.
In the netfilter callback, that runs in an "interrupt context" is possible to send packets, but is not possible to receive them (since every attempt to wait causes a kernel panic).
For this reason the solution I proposed works with UDP, but cannot work with TCP (the TCP handshake requires that the ACK message must be received).
Anyway, as already said, doing this kind of things in kernel space is BAD and should be used only for learning purposes.
Is there a way to get the sockfd from a struct sock or any other way that would allow me to uniquely identify the socket / connection I'm working with in kernel space?
I need this piece of information in the context of a device driver for a network adapter.
I thought it was impossible but actually there is a way, at least for simple cases where we have no duplicate file descriptors for a single socket. I'm answering my own question, hoping it'll help people out there.
int get_sockfd(struct sock *sk)
{
int sockfd;
unsigned int i;
struct files_struct *current_files;
struct fdtable *files;
struct socket *sock;
struct file *sock_filp;
sockfd = -1;
sock = sk->sk_socket;
sock_filp = sock->file;
current_files = current->files;
files = files_fdtable(current_files);
for (i = 0; files->fd[i] != NULL; i++) {
if (sock_filp == files->fd[i]) {
sockfd = i;
break;
}
}
return sockfd;
}
You would of course want to check for NULL pointers, starting with struct sock *sk passed in param.
So, basically, the idea is that the numerical value of a file descriptor (a sockfd is just a regular file descriptor, after all) corresponds to the index of its corresponding entry in a process open files table. All we have to do when given a struct sock *sk pointer is loop over the open files table of the current process until the addres pointed to by sk->sk_socket->file matches an entry in the table.