C
server.c
/*
* tcpserver.c - A simple TCP echo server
* usage: tcpserver <port>
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <arpa/inet.h>
#include "../uapi/unoa.h"
#define BUFSIZE 1024
#define DEFAULT_PORT 25554
void show_client_addr(char *str, struct sockaddr_in *clientaddr)
{
char ip[64] = {0};
inet_ntop(clientaddr->sin_family, &clientaddr->sin_addr.s_addr, ip, 64);
printf("%s:%s\n", str, ip);
return;
}
void error(char *msg) {
printf("%s: [%d]%s\n", msg, errno, strerror(errno));
}
void start_tcp(char *server_ip, int portno)
{
int parentfd;
int childfd; /* child socket */
socklen_t clientlen; /* byte size of client's address */
struct sockaddr_in serveraddr; /* server's addr */
struct sockaddr_in clientaddr; /* client addr */
char buf[BUFSIZE]; /* message buffer */
char *hostaddrp; /* dotted decimal host addr string */
int optval; /* flag value for setsockopt */
int n; /* message byte size */
struct toa_sockopt socktoa;
int socktoa_len = sizeof(struct toa_sockopt);
char addr[64];
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0)
error("ERROR opening socket");
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET, server_ip, &serveraddr.sin_addr.s_addr);
serveraddr.sin_port = htons((unsigned short)portno);
if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
error("ERROR on binding");
if (listen(parentfd, 5) < 0) /* allow 5 requests to queue up */
error("ERROR on listen");
clientlen = sizeof(clientaddr);
while (1) {
printf("waiting for connection.\n");
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, &clientlen);
if (childfd < 0) {
error("ERROR on accept");
continue;
}
show_client_addr("client ip from accept: ", &clientaddr);
if (getsockopt(childfd, IPPROTO_IP, TOA_CTL, &socktoa, &socktoa_len) == -1) {
error("getsockopt error");
close(childfd);
continue;
} else {
printf("socktoa.af = %hhu, port = %hu\n", socktoa.family, ntohs(socktoa.port));
const char *res = inet_ntop(socktoa.family, &socktoa.addr, addr, (socklen_t)sizeof(addr));
if (res == NULL)
printf("fail to get src ip,errno: %s\n", strerror(errno));
else
printf("client ip from sockopt: %s\n", res);
}
bzero(buf, BUFSIZE);
n = read(childfd, buf, BUFSIZE);
if (n < 0)
error("ERROR reading from socket");
hostaddrp = inet_ntoa(clientaddr.sin_addr);
if (hostaddrp == NULL)
error("ERROR on inet_ntoa\n");
printf("server received %ul/%d bytes datagram from %s\n", strlen(buf), n, hostaddrp);
n = write(childfd, buf, strlen(buf));
if (n < 0)
error("ERROR writing to socket");
close(childfd);
}
close(parentfd);
}
void start_udp(char *server_ip, int portno)
{
int parentfd;
int childfd; /* child socket */
socklen_t clientlen; /* byte size of client's address */
struct sockaddr_in serveraddr; /* server's addr */
struct sockaddr_in clientaddr; /* client addr */
char buf[BUFSIZE]; /* message buffer */
char *hostaddrp; /* dotted decimal host addr string */
int optval; /* flag value for setsockopt */
int n; /* message byte size */
struct uoa_param uparam;
socklen_t up_len = sizeof(uparam);
char saddr[64] = {0};
char daddr[64] = {0};
printf("start udp.\n");
parentfd = socket(AF_INET, SOCK_DGRAM, 0);
if (parentfd < 0)
error("ERROR opening socket");
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET, server_ip, &serveraddr.sin_addr.s_addr);
serveraddr.sin_port = htons((unsigned short)portno);
if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0)
error("ERROR on binding");
printf("listening on udp %s:%hu\n", inet_ntop(AF_INET, &serveraddr.sin_addr, saddr, BUFSIZE), ntohs(serveraddr.sin_port));
clientlen = sizeof(clientaddr);
while (1) {
bzero(buf, BUFSIZE);
n = recvfrom(parentfd, buf, BUFSIZE, 0, (struct sockaddr *) &clientaddr, &clientlen);
if (n < 0)
error("ERROR in recvfrom");
hostaddrp = inet_ntoa(clientaddr.sin_addr);
if (hostaddrp == NULL)
error("ERROR on inet_ntoa\n");
printf("server received %u/%d bytes datagram from %s : %s\n", strlen(buf), n, hostaddrp, buf);
memset(&uparam, 0, sizeof(uparam));
memcpy(&uparam.saddr, &clientaddr.sin_addr, sizeof(struct in_addr));
memcpy(&uparam.daddr, &serveraddr.sin_addr, sizeof(struct in_addr));
uparam.sport = clientaddr.sin_port;
uparam.dport = serveraddr.sin_port;
uparam.af = AF_INET;
printf("lookup uoa by using 4tuple %s:%hu %s:%hu\n",
inet_ntop(uparam.af, &uparam.saddr, saddr, BUFSIZE), ntohs(uparam.sport),
inet_ntop(uparam.af, &uparam.daddr, daddr, BUFSIZE), ntohs(uparam.dport));
if (getsockopt(parentfd, IPPROTO_IP, UOA_CTL, &uparam, &up_len) == -1) {
error("getsockopt error\n");
} else {
printf("sockuoa.af = %hhu, ip:port = %s:%hu\n", uparam.real_af, inet_ntop(uparam.real_af, &uparam.real_saddr, buf, BUFSIZE) ,ntohs(uparam.real_sport));
bzero(buf, BUFSIZE);
}
if (strlen(buf) > 1) {
n = sendto(parentfd, buf, strlen(buf), 0, (struct sockaddr *) &clientaddr, clientlen);
if (n < 0)
perror("ERROR in sendto");
}
}
close(parentfd);
}
enum protomode {
MODE_TCP = 0,
MODE_UDP = 1,
};
void start_listen(char *server, int port, int mode)
{
switch (mode) {
case MODE_TCP:
start_tcp(server, port);
break;
case MODE_UDP:
start_udp(server, port);
break;
default:
start_tcp(server, port);
break;
}
return;
}
int main(int argc, char **argv)
{
int portno;
char *server_ip = NULL;
int mode = MODE_TCP;
if (argc < 4)
mode = MODE_TCP;
if (argc < 3)
portno = DEFAULT_PORT; /* default port to listen */
if (argc < 2)
server_ip = "0.0.0.0"; /* default listen all local ip */
if (argc == 4) {
server_ip = argv[1];
portno = atoi(argv[2]);
mode = atoi(argv[3]);
}
start_listen(server_ip, portno, mode);
return 0;
}
unoa.h
#ifndef _UNOA_H_
#define _UNOA_H_
#include <linux/types.h>
#ifdef __KERNEL__
#include <linux/in.h>
#include <linux/in6.h>
#else
#include <arpa/inet.h>
#endif
#define TOA_CTL (2049) /* tcp sockopt option */
#define UOA_CTL (2048) /* udp sockopt option */
/*
* toa_sockopt 结构是通过 getsockopt 接口获取 tcp 连接真实源 ip 的返回值,
* 使用方式:getsockopt(sockfd, IPPROTO_IP, TOA_CTL, &socktoa, &socktoa_len),其中
* sockfd:连接的文件句柄,
* IPPROTO_IP:协议,固定
* TOA_CTL:option,固定
* &socktoa:struct toa_sockopt 类型的指针,用来保存 getsockopt 的返回结果,可以初始化为 0
* &socktoa_len:socktoa 变量的长度,通常设置为 sizeof(struct toa_sockopt)
*/
struct toa_sockopt {
union {
__be32 ip[1];
__be32 ip6[4];
} addr; /* 真实客户端 ip */
__be16 port; /* 真实客户端端口 */
__u8 family; /* 真实客户端 ip 类型:AF_INET / AF_INET6 */
__u8 toa_set; /* 1 表示设置了 toa */
};
union inet_addr {
struct in_addr in;
struct in6_addr in6;
};
/*
* uoa_param 结构是通过 getsockopt 接口获取 udp 连接真实源 ip 的入参 + 返回值,
* 使用方式:getsockopt(sockfd, IPPROTO_IP, UOA_SO_GET_LOOKUP, ¶m, &plen)
* sockfd:连接的文件句柄,
* IPPROTO_IP:协议,固定
* UOA_SO_GET_LOOKUP:option,固定
* ¶m:struct uoa_param 类型的指针,用来传递入参及保存 getsockopt 的返回结果,输入是五元组,输出可以初始化为 0
* &plen:param 变量的长度,通常设置为 sizeof(struct uoa_param)
*/
struct uoa_param {
/* input */
union inet_addr saddr;
union inet_addr daddr;
__be16 sport;
__be16 dport;
__u8 af;
/* output */
union inet_addr real_saddr;
__be16 real_sport;
__u8 real_af;
} __attribute__((__packed__));
#endif