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, &param, &plen)
 * sockfd:连接的文件句柄,
 * IPPROTO_IP:协议,固定
 * UOA_SO_GET_LOOKUP:option,固定
 * &param: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

results matching ""

    No results matching ""