Created
October 22, 2013 16:50
-
-
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).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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