Skip to content

Instantly share code, notes, and snippets.

@vi
Created October 22, 2013 16:50
Show Gist options
  • Save vi/7104093 to your computer and use it in GitHub Desktop.
Save vi/7104093 to your computer and use it in GitHub Desktop.
Turn a pair of packet AF_UNIX sockets into a UDP connection Primary use case: forward individual UDP stream into other network namespace though the shared filesystem (TCP case can be done with socat).
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/un.h>
char buf[4096];
union sockaddr_some {
struct sockaddr_in6 sa6;
struct sockaddr_in sa4;
};
void init_address(int ip_family, union sockaddr_some *sa, const char* host, int port) {
memset((char *) sa, 0, sizeof(*sa));
if (ip_family == AF_INET) {
struct sockaddr_in *sa_in = &sa->sa4;
sa_in->sin_family = ip_family;
sa_in->sin_port = htons(port);
unsigned char buf[sizeof(struct in_addr)];
if (inet_pton(AF_INET, host, buf)==0) {
perror("inet_aton AF_INET");
}
memcpy(&sa_in->sin_addr, buf, sizeof sa_in->sin_addr);
} else
if (ip_family == AF_INET6) {
struct sockaddr_in6 *sa_in6 = &sa->sa6;
sa_in6->sin6_family = ip_family;
sa_in6->sin6_port = htons(port);
unsigned char buf[sizeof(struct in6_addr)];
if (inet_pton(AF_INET6, host, buf)==0) {
perror("inet_aton AF_INET6");
}
memcpy(sa_in6->sin6_addr.s6_addr, buf, sizeof sa_in6->sin6_addr);
} else {
fprintf(stderr, "Unsupported address family\n");
}
}
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif
void init_address_un(struct sockaddr_un *sa, const char* path) {
memset((char*) sa, 0, sizeof(*sa));
sa->sun_family = AF_UNIX;
strncpy(sa->sun_path, path, UNIX_PATH_MAX);
sa->sun_path[UNIX_PATH_MAX-1]=0;
if(sa->sun_path[0]=='@') sa->sun_path[0]=0;
}
int s1_recv;
int s1_send;
int s2;
struct sockaddr_un peer1_recv;
struct sockaddr_un peer1_send;
union sockaddr_some peer2;
union sockaddr_some peer2_client;
int udp_socket(int ip_family) {
int s2;
if ((s2=socket(ip_family, SOCK_DGRAM, IPPROTO_UDP))==-1) {
perror("socket");
return -1;
}
return s2;
}
int unix_socket() {
int s2;
if ((s2=socket(AF_UNIX, SOCK_DGRAM, 0))==-1) {
perror("AF_UNIX socket");
return -1;
}
return s2;
}
int main(int argc, char* argv[])
{
int ip_family = AF_INET;
if (argc<6) {
fprintf(stderr, "Usage: unix2udp {l|c|l6|c6} [@]unix_socket_path_recv [@]unix_socket_path_send host1 port1\n");
return 1;
}
char UDP_listen_or_connect = argv[1][0];
char* recv_path = argv[2];
char* send_path = argv[3];
char* host1 = argv[4];
int port1 = atoi(argv[5]);
if (!UDP_listen_or_connect) {
fprintf(stderr, "unix2udp: Invalid mode\n");
return 1;
}
if (argv[1][1] == '6') ip_family = AF_INET6;
s1_recv = unix_socket();
s1_send = -1;
init_address_un(&peer1_recv, recv_path);
init_address_un(&peer1_send, send_path);
init_address(ip_family, &peer2, host1, port1);
memset(&peer2_client, 0, sizeof peer2_client);
unlink(recv_path);
if (bind(s1_recv, (struct sockaddr*)(&peer1_recv), sizeof(peer1_recv))==-1) {
perror("bind");
return -1;
}
if (UDP_listen_or_connect=='c') {
s2 = -1;
} else {
s2 = udp_socket(ip_family);
if (bind(s2, (struct sockaddr*)&peer2, sizeof(peer2))==-1) {
perror("bind");
return -1;
}
}
fcntl(s1_recv, F_SETFL, O_NONBLOCK);
fcntl(s2, F_SETFL, O_NONBLOCK);
fd_set rfds;
for(;;) {
select_repeat:
FD_ZERO(&rfds);
FD_SET(s1_recv, &rfds);
if (s2!=-1) {
FD_SET(s2, &rfds);
}
int maxfd = (s1_recv>s2)?s1_recv:s2;
int ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
if(ret==-1) {
if(errno==EINTR || errno==EAGAIN) goto select_repeat;
perror("select");
return 2;
}
if(FD_ISSET(s1_recv, &rfds)) {
ret = recv(s1_recv, &buf, sizeof(buf), 0);
if(ret==-1) {
perror("recv");
return 4;
}
if(ret) {
if (s2 == -1) {
s2 = udp_socket(ip_family);
if (UDP_listen_or_connect=='c') {
if (connect(s2, (struct sockaddr*)(&peer2), sizeof(peer2))==-1) {
perror("connect");
close(s2);
s2 = -1;
continue;
}
}
fcntl(s2, F_SETFL, O_NONBLOCK);
}
if (UDP_listen_or_connect=='c') {
ret = send(s2, &buf, ret, 0);
} else {
ret = sendto(s2, &buf, ret, 0, (struct sockaddr*)(&peer2_client), sizeof(peer2_client));
}
if (!ret || ret == -1) {
perror("send to inet");
close(s2);
s2=-1;
} else {
write(1, ">", 1);
}
}
}
if(s2!=-1 && FD_ISSET(s2, &rfds)) {
socklen_t slen = sizeof(peer2_client);
ret = recvfrom(s2, &buf, sizeof(buf), 0, (struct sockaddr*)(&peer2_client), &slen);
if(ret==-1) {
perror("recvfrom");
close(s2);
s2=-1;
continue;
}
if(ret) {
if(s1_send == -1) {
s1_send = unix_socket();
if (connect(s1_send, (struct sockaddr*)(&peer1_send), sizeof(peer1_send))==-1) {
perror("connect AF_UNIX");
close(s1_send);
s1_send = -1;
continue;
}
}
fcntl(s1_send, F_SETFL, O_NONBLOCK);
ret = send(s1_send, &buf, ret, 0);
if (!ret || ret==-1) {
perror("send to unix");
close(s1_send);
s1_send = -1;
} else {
write(1, "<", 1);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment