Skip to content →

Author: Daoyuan Li

热心的芬兰人

今天骑车去Ikea时完全没有用GPS,因为之前在Tourist Center拿了一份非常详细的户外运动地图,而且沿路给骑行者的路标也十分详细。所以我就信心十足的出发了,骑到路口的时候再拿出地图来计划路线。

当我在一个十字路口看地图的时候,一个遛着狗的女士对我说了一句芬兰语,当她看到我脸上的茫然后马上换成英语问我是不是迷路了。我说有一点点,本来我只是想知道自己现在在什么地方的,她只给我看了之后又问我要去哪儿,我说去宜家,然后她又在地图上只给我看,说有这两条路线,一条需要翻山,另一条可能平坦一点。我说翻山可能有意思一点儿吧,她说是这样,然后又详细的告诉我该怎么走,从哪儿过马路,穿过一个桥洞,再沿着医院的方向走……热情的都让我有些不好意思了。我谢过她,继续前进。

在另一个十字路口,我又拿出地图辨认路线。这时一个中年大叔从我身边骑了过去,骑过去之后他停下来,问我要去哪儿,我说去宜家。然后大叔就掉过头并对我说,跟我来吧!我很诧异的问他,你也去那儿吗?他说本来不是的,但是我就住在那附近,我可以带你去。于是我就跟着大叔走,一路上大叔给我介绍这是学校,这是他父母朋友的家,这是很受欢迎的咖啡厅……路上遇到骑着车的小学生,大叔也用芬兰语叮嘱一番,似乎是让小朋友们注意安全。骑了大概二十分钟才到,一路翻山越岭,大叔还不停的给我介绍这个地方,把我感动的一塌糊涂。

以前听同学说芬兰人比瑞典人纯朴,今天算是体会到这一点了,至少在瑞典,你不张口的话应该不会有人主动上来问你需不需要帮助的。

P.S. 这里的郊区真的很美,以后有机会要多去城市周边转转。

Leave a Comment

瑞典语小结

今天上午考完了advanced beginners的瑞典语,总结一下这一个月学习的内容。

语序:老师说在这一阶段最重要的就是语序了。在主句中永远遵循动词第二的法则,时间状语要么放在句首、要么放在句尾,地点状语可以放在句首,但是当时间在句首是地点放在句尾、时间状语放在句尾是地点放在倒数第二的位置。副词一般放在动词之后,当然也可以放在其他的位置,视强调的不同成分而定。

名词和形容词的一致性:要考虑名词是en还是ett,是单数还是复数,是定指还是泛指。形容词要随着名词作相应的改变。形容词变副词通常是加t即可。形容词比较级一般加are,最高级加ast,最高级定指加aste。

代词:注意sin、sitt、sina的用法。

特殊动词:表示知道,认识的词有vet,
kunna, känna, känna till, känna sig。表示把东西如何放在某个地方的词有sätta, ställa, lägga。

S-verb:大致可分为三类,即表示被动,表示相互性质的动作,以及一直带s的动词。

Perfect participate:动词变形容词。

组合词:由两个或两个以上名词或形容词组合而成,中间没有空格或者连字符,某些词可能要加s。

Leave a Comment

逆风,逆行

不管什么时候骑车,总感觉路边的花花草草会向我鞠躬。考虑到我本人没有花草通吃的魅力,又经过一番苦思冥想之后得出结论:不管我什么时候骑车,我总是逆风而行。也有可能是顺风的时候省力,不会去考虑为什么可以骑得这么快的问题。嗯,当我以45公里的时速骑行时,我是决计不会考虑为什么的,我考虑的可能是能不能再快一点。

昨天去市中心了,结果一进城就迷路了,中间还有一次在自行车道上逆行了。我一直以为自行车不存在逆行的问题,结果昨天就傻傻的被卡在了路中间,看着前面冲过来的车流,只好灰溜溜掉头逃命。唉,第一次进城就土了一把。

今天平生第一次补胎。中午回来时还没发现车胎爆了,等到下午准备出去时发现后胎一点气都没有。只好卸下车轮,撬开外胎,把内胎抠出来后检查破在什么地方。掏出工具,打磨车胎,涂上胶水,贴上补丁,充气检查。然后装好内外胎和车轮,顺利骑到了Barkarby在Jula买了新内胎。明天再检查一下,看看今天的补胎成果咋样。万一不行就换新轮胎。

Leave a Comment

轻车熟路

昨天修了车之后好多了,前拨链器能正常变速,下坡的时候速度可以更快了。听着耳边呼呼的风声,看着前面的路一点点消失有一点点出现,这种感觉不错。

今天去学校没有用GPS,路上也没有迷路,回来的时候还抄了近道。下午又去了趟Barkarby,仍然没有用GPS,去的时候虽然绕了点儿,但回来的时候异常顺利。

买了码表、前灯、水壶等物,还买了把长的螺丝刀和尖嘴钳,以后就算链条卡住了也不会怕了。

Leave a Comment

SNS Privacy Control

I wrote a paper on privacy control in social networking sites. You can find the full paper at http://www.cse.hut.fi/en/publications/B/11/papers/li.pdf. Here’s the abstract:

As social networking services become increasingly popular, more and more attacks against users’ private information are reported. As a result, privacy protection becomes an important concern among users. Previous research has produced many different approaches to deal with privacy control in different social networking sites. In this paper, we make a survey on different approaches proposed to tackle the privacy issue in social networking sites. In particular, we put current approaches into three general categories, i.e. approaches addressing end users’ active participation, security automation based on machine learning algorithms, and privacy preserving by using a decentralized architecture for social networkingservices. Then we introduce and analyze some of the approaches in each category. Finally, we give some suggestions that may help privacy control in online social networks.

Leave a Comment

Testing Bouncer

Static testing is used to test the bouncer, that is, a separate set of test cases and sample outputs are provided. And the bouncer is given the test cases and its outputs are compared with sample outputs.

A verifier is used to accomplish the above function, and another bash script is written to automate the process.

The bash script is as follows:

#!/bin/sh

USAGE="Usage: $0 [-v] bouncer" 
CMD="./verifier "
TESTDIR="cases"

while getopts "v" optname
do
    case "$optname" in
        "v")
        CMD="./verifier -v "
        ;;
        *)
        echo $USAGE

exit 1
        ;;
    esac
done
shift $(($OPTIND-1))
BCER=$*

if [ -f "$BCER" ]
then
    find "$TESTDIR" -iname "*pcap" | while read i
do
    if [ -f "$i" ] && [ -f "$i"-ref ]
    then
        echo "======================================================================"
        echo "Verifying file: $i"
        $BCER -t -l 1.1.1.1 -s 3.3.3.3 <"$i" >"$i"-out
	BCRT=$?
	echo
"----------------------------------------------------------------------"
        if [ $BCRT != 0 ]
        then
            echo "$BCER returned with $?"
            echo "Exiting $0"
            exit 1
        fi
        $CMD -i "$i"-out -r "$i"-ref
	CMDRT=$?
	echo "----------------------------------------------------------------------"
        echo "Result for $i:"
	echo "$CMDRT packets has passed validation"
        echo
"======================================================================"
	echo "n"
    fi
done
else
    echo $USAGE
fi

exit 0

The verifier is as follows:

#include "verifier.h"
typedef unsigned short u16;
typedef unsigned long u32;

/* CRC
 * Adopted from http://www.netfor2.com/ipsum.htm

*/
u16 ip_sum_calc(u16 len_ip_header, u16 buff[]) {
    u16 word16;
    u32 sum = 0;
    u16 i;

    // make 16 bit words out of every two adjacent 8 bit words in the packet
    // and add them up
    for (i = 0; i < len_ip_header; i = i + 2) {
        word16 = ((buff[i] << 8)&0xFF00)+(buff[i + 1]&0xFF);
        sum = sum + (u32) word16;
    }

    // take only 16 bits out of the 32 bit sum and add up the carries
    while
(sum >> 16)
        sum = (sum & 0xFFFF)+(sum >> 16);

    // one's complement the result
    sum = ~sum;

    return ((u16) sum);
}

int usage() {
    fprintf(stderr, "Usage: verifier [-v] -i input_file -r reference_filen");
    exit(1);
}

int main(int argc, char **argv) {
    int verbose = 0;
    opterr = 0;
    char c;
    char *ifilename, *rfilename;
    struct stat st;
    while ((c = getopt(argc, argv,
"vi:r:")) != -1) {
        if (c == 'i') {
            ifilename = optarg;
            if (stat(ifilename, &st) != 0) {
                fprintf(stderr, "File does not exist!n");
                exit(1);
            }
        } else if (c == 'r') {
            rfilename = optarg;
            if (stat(rfilename, &st) != 0) {
                fprintf(stderr, "File does not exist!n");
                exit(1);
            }

} else if (c == 'v') {
            verbose = 1;
        } else {
            usage();
        }
    }

    pcap_t *ihandle = NULL, *rhandle = NULL;
    char errbufi[PCAP_ERRBUF_SIZE], errbufr[PCAP_ERRBUF_SIZE];
    ihandle = pcap_open_offline(ifilename, errbufi);
    rhandle = pcap_open_offline(rfilename, errbufr);
    struct pcap_pkthdr ihdr, rhdr;
    const u_char *ipkt, *rpkt;
    ipkt = malloc(MAX_PACKET_SIZE);
    rpkt =
malloc(MAX_PACKET_SIZE);
    if (ipkt == NULL || rpkt == NULL) {
        perror("malloc");
        exit(1);
    }
    int pc = -1; /* Packet counter */
    char flags[MAX_PACKETS_NO]; /* Broken or Not */
    memset(flags, PKT_OK, MAX_PACKETS_NO);
    while ((ipkt = pcap_next(ihandle, &ihdr)) && (rpkt = pcap_next(rhandle, &rhdr))) {
        pc++;
        /* Typecasting input packet*/
        const struct
sniff_ethernet *iethernet; /* The ethernet header */
        const struct sniff_ip *iip; /* The IP header */
        const struct sniff_icmp *iicmp; /* The ICMP header */
        const char *ipayload; /* Packet payload */
        u_int isize_ip;
        iethernet = (struct sniff_ethernet*) (ipkt);
        iip = (struct sniff_ip*) (ipkt + SIZE_ETHERNET);
        isize_ip = IP_HL(iip)*4;
        if (isize_ip < 20) {
            fprintf(stderr, "   * Invalid
IP header length: %u bytesn", isize_ip);
            continue;
        }
        iicmp = (struct sniff_icmp*) (ipkt + SIZE_ETHERNET + isize_ip);
        ipayload = (char *) (ipkt + SIZE_ETHERNET + isize_ip + SIZE_ICMP);
        /* Typecasting reference packet*/
        const struct sniff_ethernet *rethernet; /* The ethernet header */
        const struct sniff_ip *rip; /* The IP header */
        const struct sniff_icmp *ricmp; /* The ICMP header */

const char *rpayload; /* Packet payload */
        u_int rsize_ip;
        rethernet = (struct sniff_ethernet*) (rpkt);
        rip = (struct sniff_ip*) (rpkt + SIZE_ETHERNET);
        rsize_ip = IP_HL(rip)*4;
        if (rsize_ip < 20) {
            fprintf(stderr, "   * Invalid IP header length: %u bytesn", rsize_ip);
            continue;
        }
        ricmp = (struct sniff_icmp*) (rpkt + SIZE_ETHERNET + rsize_ip);
        rpayload = (char *) (rpkt
+ SIZE_ETHERNET + rsize_ip + SIZE_ICMP);

        /* Here comes the validation process */
        /* Check IP checksum */
        u16 ipbuf[isize_ip];
        u16 ipsum = ntohs(iip->ip_sum);
        int i;
        for (i = 0; i < isize_ip; i++) {
            if (i == 10 || i == 11)
                ipbuf[i] = 0x00;
            else
                ipbuf[i] = *((u_char *) (ipkt + SIZE_ETHERNET + i));
        }
        if (ipsum !=
ip_sum_calc(isize_ip, ipbuf)) {
            flags[pc] = ERR_IP_SUM;
            continue;
        }
        /* Check ICMP checksum */
        int plen = ihdr.len - SIZE_ETHERNET - isize_ip - 8;
        u16 icmpbuf[8 + plen];
        u16 icmpsum = ntohs(iicmp->icmp_sum);
        for (i = 0; i < 8; i++) {
            if (i == 2 || i == 3)
                icmpbuf[i] = 0x00;
            else
                icmpbuf[i] = *((u_char *) (ipkt +
SIZE_ETHERNET + isize_ip + i));
        }
        for (i = 0; i < plen; i++) {
            icmpbuf[i + 8] = *((u_char *) (ipayload + i));
        }
        if (icmpsum != ip_sum_calc(8 + plen, icmpbuf)) {
            flags[pc] = ERR_ICMP_SUM;
            continue;
        }
        /* Check IP TTL */
        if (iip->ip_ttl <= 0) {
            flags[pc] = ERR_IP_TTL;
            continue;
        }
        /* Check IP source
address */
        if (iip->ip_src.s_addr != rip->ip_src.s_addr) {
            flags[pc] = ERR_IP_SADDR;
            continue;
        }
        /* Check IP destinataion address */
        if (iip->ip_dst.s_addr != rip->ip_dst.s_addr) {
            flags[pc] = ERR_IP_DADDR;
            continue;
        }

        /* Check ICMP type */
        if (iicmp->icmp_type != ricmp->icmp_type) {
            flags[pc] = ERR_ICMP_TYPE;

continue;
        }
        /* Check ICMP ID */
        if (iicmp->icmp_id != ricmp->icmp_id) {
            flags[pc] = ERR_ICMP_ID;
            continue;
        }
        /* Check ICMP sequence number */
        if (iicmp->icmp_sequence != ricmp->icmp_sequence) {
            flags[pc] = ERR_ICMP_SEQ;
            continue;
        }
    }
    pc++;
    int bc = 0; /* Broken packet counter */
    int i;
    for (i = 1; i <= pc;
i++) {
        if (flags[i-1])
            bc++;
        if (verbose) {
            switch (flags[i-1]) {
                case PKT_OK:
                    fprintf(stdout, "Validation passed: %d/%dn", i, pc);
                    break;
                case ERR_IP_SUM:
                    fprintf(stdout, "IP checksum failed: %d/%dn", i, pc);
                    break;
                case ERR_IP_TTL:
                    fprintf(stdout, "IP TTL check
failed: %d/%dn", i, pc);
                    break;
                case ERR_IP_SADDR:
                    fprintf(stdout, "IP source address check failed: %d/%dn", i, pc);
                    break;
                case ERR_IP_DADDR:
                    fprintf(stdout, "IP destination address check failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_SUM:
                    fprintf(stdout, "ICMP checksum
failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_TYPE:
                    fprintf(stdout, "ICMP type check failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_ID:
                    fprintf(stdout, "IP ID check failed: %d/%dn", i, pc);
                    break;
                case ERR_ICMP_SEQ:
                    fprintf(stdout, "IP sequence check failed: %d/%dn", i, pc);

break;
                default:
                    fprintf(stderr, "ERROR: Unknwon error type.n");
                    break;
            }
        }
    }
    if (bc == 0)
        fprintf(stdout, "%d packets passed validationn", pc);
    else
        fprintf(stdout, "%d out of %d packets failed validationn", bc, pc);
    /* Return number of packets passed validation */
    return (pc - bc);
}
Leave a Comment

Technical Details

We define a BPF filter as “icmp and dst host <bouncer_ip>”, which filters out all none-ICMP packets and packets not destined to the bouncer. The we compile this filter and set the filter to the capture device.

When the capture device captures a packet, a process_pkt function is called to process the packet. And that is where we validate the packets and then, if the packet is valid, update
the packet and then send it out, or write the packet to a dump file.

The process of validating the packets are as follows:

  1. Validate checksum of IP header.
  2. Validate TTL of IP.
  3. Validate IP source address
  4. Validate ICMP header checksum. 
  5. Validate ICMP type and code. 

Since we write the packet to a dump file in a separate function, so a pointer to the dump file handler is passed to the processing function when the process_pkt function is called. The same method is used to pass server IP address and test mode flag.

We keep a linked list of all the ICMP echo requests. When a ICMP echo reply is received, we go through the linked list to find out where
the original echo request comes from.

The code is as follows.

bouncer.h:

/* Global definitions for the port bouncer
 * Packet headers and so on
 */

#define _BSD_SOURCE 1

#include 
#include 
#include 
#include 
#include 

/* PCAP declarations*/
#include 

/* Standard networking declaration */
#include 
#include 
#include 

/*
 * The following system include files should provide you with the 
 * necessary declarations for Ethernet, IP, and TCP headers
 */

#include 
#include 
#include 
#include 

/* Add any otherdeclarations you may need here... */

#define MAX_PACKET_SIZE 65535

/* Ethernet addresses are 6 bytes */
//#define ETHER_ADDR_LEN	6

/* Ethernet header */
struct sniff_ethernet {
    u_char ether_dhost[ETHER_ADDR_LEN]; /* Destination host address */
    u_char ether_shost[ETHER_ADDR_LEN]; /* Source host address */
    u_short ether_type; /* IP? ARP? RARP? etc */
};

/* IP header */
struct sniff_ip {
    u_char ip_vhl; /*version << 4 | header length >> 2 */
    u_char ip_tos; /* type of service */
    u_short ip_len; /* total length */
    u_short ip_id; /* identification */
    u_short ip_off; /* fragment offset field */
#define IP_RF 0x8000		/* reserved fragment flag */
#define IP_DF 0x4000		/* dont fragment flag */
#define IP_MF 0x2000		/* more fragments flag */
#define IP_OFFMASK 0x1fff	/* mask for fragmenting bits */
    u_char ip_ttl; /*time to live */
    u_char ip_p; /* protocol */
    u_short ip_sum; /* checksum */
    struct in_addr ip_src, ip_dst; /* source and dest address */
};
#define IP_HL(ip)		(((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)		(((ip)->ip_vhl) >> 4)

/* ICMP header */
struct sniff_icmp {
    u_char icmp_type;
#define ICMP_ECHO 0x8
#define ICMP_REPLY 0x0
    u_char icmp_code;
    u_int16_t icmp_sum;
    u_int16_t icmp_id;
u_int16_t icmp_sequence;
};

#define SIZE_ETHERNET 14
#define SIZE_ICMP 8

#define IP_QUAD(ip)  (ip)>>24,((ip)&0x00ff0000)>>16,((ip)&0x0000ff00)>>8,((ip)&0x000000ff)

struct request{
    struct request* next;
    struct sniff_ip* ip;
    struct sniff_icmp* icmp;
};

process_pkt.c:

#include "bouncer.h"

/* CRC
 * Adopted from http://www.netfor2.com/ipsum.htm
 */
typedef unsigned short u16;
typedef unsigned long u32;

u16 ip_sum_calc(u16 len_ip_header, u16 buff[]) {
    u16 word16;
    u32 sum =0;
    u16 i;

    // make 16 bit words out of every two adjacent 8 bit words in the packet
    // and add them up
    for (i = 0; i < len_ip_header; i = i + 2) {
        word16 = ((buff[i] << 8)&0xFF00)+(buff[i + 1]&0xFF);
        sum = sum + (u32) word16;
    }

    // take only 16 bits out of the 32 bit sum and add up the carries
    while (sum >> 16)
        sum = (sum & 0xFFFF)+(sum >> 16);

    // one'scomplement the result
    sum = ~sum;

    return ((u16) sum);
}

void process_pkt(u_char *args, struct pcap_pkthdr *header,
        u_char *packet) {
    if(header->caplen < header->len){
        fprintf(stderr, "ERROR: Packet data not captured completely\n");
        return;
    }
    /* Main function to perform the bouncing */
    u_char testmode = *args;
    char *serv_addr = (char *) (args + 1);
    u_int32_t dst_addr =inet_addr(serv_addr);
    //pcap_dumper_t *dumper = NULL;
    u_char *dumper = NULL;
    memcpy(&dumper, args + 2 + strlen(serv_addr), sizeof (pcap_dumper_t *));
    struct request ** prequests = NULL;
    memcpy(&prequests, args + 2 + strlen(serv_addr) + sizeof (pcap_dumper_t *), sizeof (struct request**));
    //struct request *requests = *prequests;

    /* Typecasting packet*/
    struct sniff_ethernet *ethernet; /* The ethernet header */
struct sniff_ip *ip; /* The IP header */
    struct sniff_icmp *icmp; /* The ICMP header */
    char *padding; /* Packet padding */
    u_int size_ip;
    ethernet = (struct sniff_ethernet*) (packet);
    ip = (struct sniff_ip*) (packet + SIZE_ETHERNET);
    size_ip = IP_HL(ip)*4;
    u_int32_t srcip = ntohl(ip->ip_src.s_addr);
    if (size_ip < 20 || size_ip > header->len - SIZE_ETHERNET) {
        fprintf(stderr, "ERROR: Invalid IP header length: %ubytes\n", size_ip);
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    icmp = (struct sniff_icmp*) (packet + SIZE_ETHERNET + size_ip);
    padding = (u_char *) (packet + SIZE_ETHERNET + size_ip + SIZE_ICMP);

    /* Validate the packet */
    /* Validate IP header */
    /* Check IP version */
    u_char v = ip->ip_vhl>> 4;
    if(v != 4){
        fprintf(stderr, "ERROR: Wrong IP verison %d\n", v);
        return;
    }
    /* Validate CRC */
    u16 ipbuf[size_ip];
    u16 ipsum = ntohs(ip->ip_sum);
    int i;
    for (i = 0; i < size_ip; i++) {
        if (i == 10 || i == 11)
            ipbuf[i] = 0x00;
        else
            ipbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + i));
    }
    if (ipsum != ip_sum_calc(size_ip, ipbuf)) {
fprintf(stderr, "ERROR: ip checksum mismatch. Dropping packet from %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    /* Validate TTL */
    if (ip->ip_ttl <= 0) {
        fprintf(stderr, "ERROR: ip TTL expired. Dropping packet from %u.%u.%u.%u\n",
IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    /* Validate IP source address */
    if (srcip == 0x00000000 || srcip >= 0xE0000000
            || (srcip & 0x000000FF) == 0x000000FF || (srcip & 0x000000FF) == 0x00000000) {
        fprintf(stderr, "ERROR: ip source address invalid. Dropping packetfrom %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }

    /* Validate ICMP header */
    /* Validate ICMP type and code */
    if (!(icmp->icmp_code == 0 && (icmp->icmp_type == 0 || icmp->icmp_type == 8))) {
        fprintf(stderr, "ERROR: icmp type orcode unsupported. Dropping packet from %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if (testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }
    /* Validate CRC */
    int plen = header->len - SIZE_ETHERNET - size_ip - 8;
    u16 icmpbuf[8 + plen];
    u16 icmpsum = ntohs(icmp->icmp_sum);
    for (i = 0; i < 8;i++) {
        if (i == 2 || i == 3)
            icmpbuf[i] = 0x00;
        else
            icmpbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + size_ip + i));
    }
    for (i = 0; i < plen; i++) {
        icmpbuf[i + 8] = *((u_char *) (padding + i));
    }
    if (icmpsum != ip_sum_calc(8 + plen, icmpbuf)) {
        fprintf(stderr, "ERROR: icmp checksum mismatch. Dropping packet from %u.%u.%u.%u\n",
                IP_QUAD(srcip));
        if(testmode == 1) {
            header->caplen = 0;
            header->len = 0;
            pcap_dump(dumper, header, packet);
        }
        return;
    }

    /* Update the packet */
    if (icmp->icmp_type == 0) {
        /* ICMP echo reply */
        ip->ip_src.s_addr = ip->ip_dst.s_addr;
        /* Search the linked list for client address */
        if (*prequests == NULL) {
            fprintf(stderr, "ERROR: process_pkt: nullrequest linked list\n");
            return;
        } else {
            /* Find where the request comes from */
            struct request *r;
            for (r = *prequests; r != NULL; r = r->next) {
                if (r->icmp->icmp_id == icmp->icmp_id && r->icmp->icmp_sequence == icmp->icmp_sequence) {
                    ip->ip_dst.s_addr = r->ip->ip_src.s_addr;
                    break;
                }
}
            if (r == NULL) {
                fprintf(stderr, "ERROR: no match echo requests in stack\n");
                return;
            }
            /* Remove the request from the linked list */
            struct request *rr = *prequests;
            if (rr == r) {
                free(rr->icmp);
                free(rr->ip);
                *prequests = r->next;
                free(r);
            } else {
                while (rr!= r)
                    rr = rr->next;
                free(rr->icmp);
                free(rr->ip);
                rr->next = r->next;
                free(r);
            }
        }
    } else {
        /* ICMP echo request */
        /* Add new request to linked list */
        struct request *r;
        r = malloc(sizeof (struct request));
        if (r == NULL) {
            perror("ERROR: process_pkt:malloc");
            return;
        }
        struct sniff_ip *ipt = malloc(sizeof (struct sniff_ip));
        if (ipt == NULL) {
            perror("ERROR: process_pkt: malloc");
            return;
        }
        memcpy(ipt, ip, sizeof (struct sniff_ip));
        r->ip = ipt;
        struct sniff_icmp* icmpt = malloc(sizeof (struct sniff_icmp));
        if (icmpt == NULL) {
            perror("ERROR: process_pkt: malloc");
return;
        }
        memcpy(icmpt, icmp, sizeof (struct sniff_icmp));
        r->icmp = icmpt;
        r->next = *prequests;
        *prequests = r;
        /* Update destination address */
        ip->ip_src.s_addr = ip->ip_dst.s_addr;
        ip->ip_dst.s_addr = dst_addr;
    }
    /* Recaculate CRC */
    for (i = 0; i < size_ip; i++) {
        if (i == 10 || i == 11)
            ipbuf[i] = 0x00;
        else
ipbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + i));
    }
    ip->ip_sum = htons(ip_sum_calc(size_ip, ipbuf));
    if (testmode == 0) {
        pcap_t *handle = NULL;
        char errbuf[PCAP_ERRBUF_SIZE], *device = "tap0";
        memset(errbuf, 0, PCAP_ERRBUF_SIZE);
        if ((handle = pcap_open_live(device, MAX_PACKET_SIZE, 1, 512, errbuf)) == NULL) {
            fprintf(stderr, "ERROR: %s\n", errbuf);
            exit(1);
        }
        /*Send the packet to network */
        if (pcap_sendpacket(handle, packet, header->len) != 0) {
            perror("ERROR: process_pkt: pcap_sendpacket");
            exit(1);
        }
        pcap_close(handle);
        fprintf(stderr, "Bouncer: packet sent\n");
    } else {
        /* Or put it back on stdout */
        int i = 0;
        for (i = 0; i < ETHER_ADDR_LEN; i++)
            ethernet->ether_shost[i] = 1;
        if(icmp->icmp_type == 0) {
            for (i = 0; i < ETHER_ADDR_LEN; i++)
                ethernet->ether_dhost[i] = 2;
        } else if (icmp->icmp_type == 8) {
            for (i = 0; i < ETHER_ADDR_LEN; i++)
                ethernet->ether_dhost[i] = 3;
        }
        pcap_dump(dumper, header, packet);

////////////////////////////////////////////////////////////////////////
/*
        pcap_t *handle = NULL;
        char errbuf[PCAP_ERRBUF_SIZE], *device = "tap0";
        memset(errbuf, 0, PCAP_ERRBUF_SIZE);
        if ((handle = pcap_open_live(device, MAX_PACKETS_NO, 1, 512, errbuf)) == NULL) {
            fprintf(stderr, "ERROR: %s\n", errbuf);
            exit(1);
        }
        if (pcap_sendpacket(handle, packet, header->len) != 0) {
perror("ERROR: process_pkt: pcap_sendpacket");
            exit(1);
        }
        pcap_close(handle);
        fprintf(stderr, "Packet sent\n");
*/
        ////////////////////////////////////////////////////////////////////////

    }
    return;
}
Leave a Comment

Packet Bouncer Overview

A bouncer bounces packets it receives from clients to servers. This is useful when people want to hide their internal network topologies. In this report, we examine a very simple case of bouncer — a bouncer that only bounces ICMP requests. When it receives an ICMP echo request, it modify the packet and forward it to the server specified by the user. After receiving the echo reply packet, it
again modifies the packet and finds out where to send the packet and then sends out the reply packet.

Leave a Comment

Code

The code looks as follows.


#include 
#include 
#include 
#include 
#include
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "event.h"
#include "rudp.h"
#include "rudp_api.h"

//#define DROP 3

rudp_socket_t rs_head = NULL;

peer_t find_peer(peer_t *firstp, structsockaddr_in* sa) {
    peer_t p;
    //int pc = 0;
    for (p = *firstp; p != NULL; p = p->next) {
        //printf("peer count: %d\n", ++pc);
        if (memcmp(p->sa, sa, sizeof (struct sockaddr_in)) == 0)
            return p;
    }
    //printf("peer not found, creating new peer\n");
    p = malloc(sizeof (struct peer));
    if (p == NULL) {
        perror("find_peer: malloc");
        return NULL;
    }
    struct sockaddr_in*sin;
    sin = malloc(sizeof (struct sockaddr_in));
    if (sin == NULL) {
        perror("peerfind: malloc");
        return NULL;
    }
    memcpy(sin, sa, sizeof (struct sockaddr_in));
    p->sa = sin;
    p->packets = NULL;
    p->status = PEER_STATUS_INIT;
    p->sb = 0;
    p->sm = RUDP_WINDOW - 1;
    p->sn = 0;
    p->next = (*firstp);
    *firstp = p;
    return p;
}

int remove_peer(peer_t *firstp,peer_t rp) {
    peer_t p, *pp;
    pp = firstp;
    for (p = *firstp; p != NULL; p = p->next) {
        if (p == rp) {
            *pp = rp->next;
            free(rp->sa);
            free(rp);
            return 0;
        }
        pp = &p->next;
    }
    return -1;
}

/*
 * Add packet into a linked list.
 * Inserted in a way that packets with smaller seqno are always placed in front
 */
int add_packet(packet_t*packet_head, packet_t *pkt) {
    if (*packet_head == NULL) {
        (*pkt)->next = *packet_head;
        *packet_head = *pkt;
    } else {
        packet_t *p = malloc(sizeof (packet_t));
        for ((*p) = *packet_head; (*p) != NULL; (*p) = (*p)->next) {
            if ((*pkt)->header->seqno == (*p)->header->seqno + 1) {
                (*pkt)->next = (*p)->next;
                (*p)->next = *pkt;
                break;
}
        }
        //return -1;
    }
    //printf("packet added: %d type %d\n", (*pkt)->header->seqno, (*pkt)->header->type);
    return 0;
}

/*
 * Remove a packet from a linked list
 */
int remove_packet(packet_t *packet_head, packet_t pkt) {
    packet_t p, *pp;
    pp = packet_head;
    for (p = *packet_head; p != NULL; p = p->next) {
        if (pkt == p) {
            //printf("packet seq no %d removed\n",pkt->header->seqno);
            *pp = p->next;
            free(p);
            return 0;
        }
        pp = &p->next;
    }
    return -1;
}

/*
 * rudp_socket: Create a RUDP socket.
 * May use a random port by setting port to zero.
 */

rudp_socket_t rudp_socket(int port32) {
    int sockfd;
    sockfd = socket(PF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("rudp_socket: socket");
return NULL;
    }
    struct sockaddr_in *sin;
    sin = malloc(sizeof (struct sockaddr_in));
    if (sin == NULL) {
        perror("rudp_socket: malloc");
        return NULL;
    }
    sin->sin_addr.s_addr = htonl(INADDR_ANY);
    //sin->sin_addr.s_addr = inet_addr("127.0.0.1");
    sin->sin_family = AF_INET;
    uint16_t port = port32;
    if (port == 0) {
        srand(time(0));
        port = (rand() % (65535 - 1024)) + 1024;
sin->sin_port = htons(port);
        while (bind(sockfd, (struct sockaddr*) sin, sizeof (struct sockaddr_in)) < 0) {
            port = (rand() % (65535 - 1024)) + 1024;
            sin->sin_port = htons(port);
        }
    } else {
        sin->sin_port = htons(port);
        if (bind(sockfd, (struct sockaddr*) sin, sizeof (struct sockaddr_in)) < 0) {
            perror("rudp_socket: bind");
            return NULL;
        }
}
    //printf("rudp_socket: bind successfully on port: %d\n", port);
    rudp_socket_t rs;
    rs = malloc(sizeof (struct rudp_socket));
    if (rs == NULL) {
        perror("rudp_socket: malloc");
        return NULL;
    }
    rs->sockfd = sockfd;
    rs->peers = NULL;
    rs->next = rs_head;
    rs_head = rs;
    /*
        int rc = 0;
        rudp_socket_t prs;
        for(prs = rs_head; prs != NULL; prs =prs->next)
            printf("rudp socket count: %d\n", ++rc);
     */
    event_fd(rs->sockfd, rudp_listen, rs, "rudp_listen");
    return rs;
}

/*
 * Listens on the socket
 */
int rudp_listen(int fd, void *arg) {
    rudp_socket_t rs;
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        if (rs->sockfd == fd)
            break;
    }
    if (rs == NULL) {
        return -1;
    }
    int (*handler)(rudp_socket_t,struct sockaddr_in *, char *, int);

    /*
        client_addr.sin_family = AF_INET;
        client_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        client_addr.sin_port = htons(0);
     */
    unsigned int clientlen = sizeof(struct sockaddr);
    struct sockaddr client_sockaddr;
    int bufsize = RUDP_MAXPKTSIZE + sizeof (struct rudp_hdr);
    int msglen = 0;
    char message[bufsize];
    memset(message, 0, bufsize);
    msglen =recvfrom(rs->sockfd, message, bufsize, 0, & client_sockaddr, &clientlen);
    if (msglen < 0) {
        perror("rudp_listen: recvfrom");
        return -1;
    }
    struct sockaddr_in client_addr;
    memcpy(&client_addr, &client_sockaddr, clientlen);
    if (ntohs(client_addr.sin_port) == 0) {
        perror("rudp_listen: recvfrom port 0");
        return -1;
    }
    //printf("received from port: %d\n",ntohs(client_addr.sin_port));
    peer_t p = find_peer(&(rs->peers), &client_addr);
    if (p == NULL) {
        return -1;
    }
    //printf("peer: %d\n", ntohs(p->sa->sin_port));
    if (msglen < 0) {
        perror("rudp_recvfrom: recvfrom");
        return -1;
    } else if (msglen < (sizeof (struct rudp_hdr))) {
        fprintf(stderr, "rudp_recvfrom: error receiving packet");
        return -1;
    }
    structrudp_hdr *msgheader = malloc(sizeof (struct rudp_hdr));
    if (msgheader == NULL) {
        perror("rudp_recvfrom: malloc");
        return -1;
    }
    memcpy(msgheader, message, sizeof (struct rudp_hdr));

    //printf("status=%d sn=%d\n", p->status, p->sn);
    //printf("message from port %d, type=%d seqno=%d\n", ntohs(p->sa->sin_port), msgheader->type, msgheader->seqno);
    struct rudp_hdr *replyheader = malloc(sizeof (structrudp_hdr));
    if (replyheader == NULL) {
        perror("rudp_recvfrom: malloc");
        return -1;
    }
    replyheader->version = RUDP_VERSION;
    switch (p->status) {
        case PEER_STATUS_INIT:
            if (msgheader->type == RUDP_SYN) {
                p->status = PEER_STATUS_ACTIVE;
                p->sn = msgheader->seqno + 1;
                replyheader->seqno = msgheader->seqno + 1;
replyheader->type = RUDP_ACK;
                sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                        (struct sockaddr*) & client_addr, clientlen);
                //printf("ACK sent. Turns active\n");
            }
            break;
        case PEER_STATUS_PASSIVE:
            if (msgheader->type == RUDP_ACK && msgheader->seqno == 1) {
                p->status = PEER_STATUS_ACTIVE;
                packet_t pkt;
for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                    if (pkt->header->seqno == msgheader->seqno - 1) {
                        pkt->status = ACKED;
                        //printf("syn packet no. %d acked\n", pkt->header->seqno);
                        break;
                    }
                }
                //p->sn = 1;
                p->sb = 1;
                p->sm = RUDP_WINDOW;
    }
            break;
        case PEER_STATUS_ACTIVE:
            switch (msgheader->type) {
                case RUDP_ACK:
                    //printf("sb: %d sm: %d\n", p->sb, p->sm);
                    if (msgheader->seqno >= p->sb) {
                        packet_t pkt;
                        for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                            if (pkt->header->seqno == msgheader->seqno -    1) {
                                pkt->status = ACKED;
                                //printf("data packet no. %d acked\n", pkt->header->seqno);
                                break;
                            }
                        }
                        p->sm += msgheader->seqno - p->sb;
                        p->sb = msgheader->seqno;
                    }
                    break;
    case RUDP_DATA:
#ifdef DROP
                    srand(time(0));
                    if (rand() % DROP == 0) {
                        printf("packet dropped\n");
                        break;
                    }
#endif
                    if (msgheader->seqno == p->sn) {
                        p->sn = msgheader->seqno + 1;
                        replyheader->seqno = msgheader->seqno + 1;
    replyheader->type = RUDP_ACK;
                        handler = malloc(sizeof (int (*)()));
                        if (handler == NULL) {
                            perror("rudp_listen: malloc");
                            return -1;
                        }
                        handler = (int (*)(rudp_socket_t, struct sockaddr_in*, char *, int))arg;
                        void *data;
                        data = malloc(msglen -    sizeof (struct rudp_hdr));
                        if (data == NULL) {
                            perror("rudp_listen: malloc");
                            return -1;
                        }
                        memcpy(data, message + sizeof (struct rudp_hdr),
                                msglen - sizeof (struct rudp_hdr));
                        handler(rs, &client_addr, data, msglen - sizeof (struct    rudp_hdr));
                        sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                                (struct sockaddr*) & client_addr, clientlen);
                    }
                    break;
                case RUDP_SYN:
                    p->status = PEER_STATUS_ACTIVE;
                    p->sn = msgheader->seqno + 1;
                    replyheader->seqno = msgheader->seqno + 1;
    replyheader->type = RUDP_ACK;
                    sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                            (struct sockaddr*) & client_addr, clientlen);
                    break;
                case RUDP_FIN:
                    p->status = PEER_STATUS_CLOSED;
                    p->sn = msgheader->seqno + 1;
                    replyheader->seqno = msgheader->seqno + 1;
    replyheader->type = RUDP_ACK;
                    sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                            (struct sockaddr*) & client_addr, clientlen);
                    break;
                default:
                    fprintf(stderr, "rudp_listen: invalid RUDP type %d\n",
                            msgheader->type);
                    break;
            }
            break;
        case    PEER_STATUS_CLOSING:
            if (msgheader->type == RUDP_ACK && msgheader->seqno == p->sn + 1) {
                p->status = PEER_STATUS_CLOSED;
                //printf("peer closed\n");
            }
            break;
        case PEER_STATUS_CLOSED:
            if (msgheader->type == RUDP_FIN) {
                p->status = PEER_STATUS_CLOSED;
                p->sn = msgheader->seqno + 1;
    replyheader->seqno = msgheader->seqno + 1;
                replyheader->type = RUDP_ACK;
                sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                        (struct sockaddr*) & client_addr, clientlen);
            }
            /*
                        if (msgheader->type == RUDP_SYN) {
                            p->status = PEER_STATUS_ACTIVE;
    p->sn = msgheader->seqno + 1;
                            replyheader->seqno = msgheader->seqno + 1;
                            replyheader->type = RUDP_ACK;
                            sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                                    (struct sockaddr*) & client_addr, clientlen);
                        } else if (msgheader->type == RUDP_FIN) {
                            p->status =    PEER_STATUS_CLOSED;
                            p->sn = msgheader->seqno + 1;
                            replyheader->seqno = msgheader->seqno + 1;
                            replyheader->type = RUDP_ACK;
                            sendto(fd, replyheader, sizeof (struct rudp_hdr), 0,
                                    (struct sockaddr*) & client_addr, clientlen);
                        }
             */
            break;
    default:
            fprintf(stderr, "rudp_listen: invalid peer status %d\n",
                    p->status);
            break;
    }
    return 0;
}

/*
 * Keep checking if there are packets to be sent out
 */
int rudp_send(int fd, void *arg) {
    event_timeout_delete(rudp_send, arg);
    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    rudp_socket_t rs;
    peer_t p;
    packet_t pkt;
    void *msg;
    int len = sizeof (struct rudp_hdr);
    //int rsc = 0;
    if (rs_head == NULL) {
        return 0;
    }
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        //printf("rudp socket count: %d\n", ++rsc);
        for (p = rs->peers; p != NULL; p = p->next) {
            if (p->status == PEER_STATUS_CLOSED) {
    if (remove_peer(&(rs->peers), p) < 0) {
                    perror("rudp_send: remove_peer");
                    return -1;
                } else {
                    event_timeout(t, rudp_send, NULL, "rudp_send");
                }
                return 0;
            } else if (p->status != PEER_STATUS_ACTIVE) {
                event_timeout(t, rudp_send, NULL, "rudp_send");
                return 0;
            }
        //int pc = 0;
            for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                //printf("pakcet %d: seq number %d status %d\n", ++pc, pkt->header->seqno, pkt->status);
                if (pkt->status == ACKED) {
                    if (remove_packet(&(p->packets), pkt) == -1) {
                        return -1;
                    }
                    event_timeout(t, rudp_send, NULL, "rudp_send");
        return 0;
                }
                if ((pkt->status == IN_QUEUE || pkt->status == TIMEOUT) &&
                        pkt->header->seqno >= p->sb &&
                        pkt->header->seqno <= p->sm) {
                    if (pkt->header->type == RUDP_FIN) {
                        p->status = PEER_STATUS_CLOSING;
                    }
                    len = sizeof (struct        rudp_hdr) + pkt->datalen;
                    msg = malloc(len);
                    if (msg == NULL) {
                        perror("rudp_send: malloc");
                        return -1;
                    }
                    memcpy(msg, pkt->header, sizeof (struct rudp_hdr));
                    memcpy(msg + sizeof (struct rudp_hdr), pkt->data, pkt->datalen);
                    sendto(rs->sockfd, msg,        len, 0,
                            (struct sockaddr*) p->sa, sizeof (struct sockaddr));
                    pkt->status = SENT;
                    pkt->trans_count++;
                    struct timeval now, interval, t;
                    gettimeofday(&now, NULL);
                    if (RUDP_TIMEOUT >= 1000) {
                        interval.tv_sec = RUDP_TIMEOUT / 1000;
                        interval.tv_usec = 0;
        } else {
                        interval.tv_sec = 0;
                        interval.tv_usec = RUDP_TIMEOUT * 1000;
                    }
                    timeradd(&now, &interval, &t);
                    event_timeout(t, rudp_timeout, pkt, "rudp_timeout");
                }

            }
        }
    }
    event_timeout(t, rudp_send, NULL, "rudp_send");
    return 0;
}

int rudp_timeout(int a, void *arg) {
        packet_t pkt = (packet_t) arg;
    event_timeout_delete(rudp_timeout, pkt);
    if (pkt->status != ACKED) {
        //printf("Timeout: %d\n", pkt->header->seqno);
        pkt->status = TIMEOUT;
        if (pkt->trans_count >= RUDP_MAXRETRANS)
            pkt->status = FAILED;
    }
    return 0;
}

/*
 *rudp_close: Close socket
 */

int rudp_close(rudp_socket_t rsocket) {

    struct timeval now, interval,        t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_close_session, rsocket, "rudp_close_socket");
    return 0;
    /*
        peer_t p;
        packet_t pkt;
        struct rudp_hdr *hdr;
        hdr = malloc(sizeof (struct rudp_hdr));
        if (hdr == NULL) {
            perror("rudp_sendto: malloc");
        return -1;
        }
        pkt = malloc(sizeof (struct packet));
        if (pkt == NULL) {
            perror("rudp_sendto: malloc");
            return -1;
        }
        for (p = rsocket->peers; p != NULL; p = p->next) {
            hdr->version = RUDP_VERSION;
            hdr->type = RUDP_FIN;
            hdr->seqno = p->sn++;
            pkt->data = NULL;
            pkt->datalen = 0;
        pkt->header = hdr;
            pkt->next = NULL;
            pkt->trans_count = 0;
            pkt->status = IN_QUEUE;
            if (add_packet(&p->packets, &pkt) < 0) {
                perror("rudp_sendto: add_packet");
                return -1;
            }
        }
        return 0;
     */
}

int rudp_close_session(int fd, void *arg) {
    event_timeout_delete(rudp_close_session, arg);
    rudp_socket_t rs        = (rudp_socket_t) arg;
    peer_t p;
    //printf("closing rudp socket %d\n", rs->sockfd);
    //printf("peer: %p\n", rs->peers);
    if (rs->peers == NULL) {
        //printf("peer null\n");
        event_fd_delete(rudp_listen, rs);
        close(rs->sockfd);
        rudp_socket_t r, *rr;
        rr = &rs_head;
        for (r = rs_head; r != NULL; r = r->next) {
            if (r == rs) {
                *rr = r->next;
        free(r);
                return 0;
            }
            rr = &r->next;
        }
        return -1;
    }
    //printf("peers not null\n");
    for (p = rs->peers; p != NULL; p = p->next) {
        //check if there are still packets to be sent
        if (p->packets != NULL) {
            struct timeval now, interval, t;
            gettimeofday(&now, NULL);
            interval.tv_sec = 0;
        interval.tv_usec = RUDP_CHECK_INTERVAL;
            timeradd(&now, &interval, &t);
            event_timeout(t, rudp_close_session, arg, "rudp_close_socket");
            return 0;
        }
        //printf("no packets in this peer, status %d\n", p->status);
        if (p->status != PEER_STATUS_CLOSED) {
            p->status = PEER_STATUS_CLOSING;
            struct rudp_hdr header;
            header.seqno = p->sn;
        header.type = RUDP_FIN;
            header.version = RUDP_VERSION;
            sendto(rs->sockfd, &header, sizeof (struct rudp_hdr), 0,
                    (struct sockaddr*) p->sa, sizeof (struct sockaddr_in));
            struct timeval now, interval, t;
            gettimeofday(&now, NULL);
            if (RUDP_TIMEOUT >= 1000) {
                interval.tv_sec = RUDP_TIMEOUT / 1000;
                interval.tv_usec = 0;
        } else {
                interval.tv_sec = 0;
                interval.tv_usec = RUDP_TIMEOUT * 1000;
            }
            timeradd(&now, &interval, &t);
            event_timeout(t, rudp_close_session, arg, "rudp_close_socket");
            //printf("FIN sent, check again 2s later\n");
            return 0;
        }
        //printf("peer status closed\n");
        if (remove_peer(&(rs->peers), p) < 0)        {
            perror("rudp_close_session: remove_peer");
            return -1;
        } else {
            //printf("peer successfully removed\n");
            struct timeval now, interval, t;
            gettimeofday(&now, NULL);
            interval.tv_sec = 0;
            interval.tv_usec = RUDP_CHECK_INTERVAL;
            timeradd(&now, &interval, &t);
            event_timeout(t, rudp_close_session, arg, "rudp_close_socket");
        return 0;
        }
    }
    return 0;
}

/*
 *rudp_recvfrom_handler: Register receive callback function
 */

int rudp_recvfrom_handler(rudp_socket_t rsocket,
        int (*handler)(rudp_socket_t, struct sockaddr_in *,
        char *, int)) {
    event_fd(rsocket->sockfd, rudp_listen, handler, "rudp_listen");
    event_fd_delete(rudp_listen, rsocket);
    return 0;
}

/*
 *rudp_event_handler: Register event handler        callback function
 */
int rudp_event_handler(rudp_socket_t rsocket,
        int (*handler)(rudp_socket_t, rudp_event_t,
        struct sockaddr_in *)) {
    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_check_status, handler, "rudp_check_status");
    return 0;
}

int        rudp_check_status(int fd, void * arg) {
    event_timeout_delete(rudp_check_status, arg);
    int (*handler)(rudp_socket_t, rudp_event_t, struct sockaddr_in *);
    handler = (int (*)(rudp_socket_t, rudp_event_t, struct sockaddr_in *))arg;
    rudp_socket_t rs;
    peer_t p;
    packet_t pkt;
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        for (p = rs->peers; p != NULL; p = p->next) {
            if (p->status == PEER_STATUS_CLOSED)        {
                handler(rs, RUDP_EVENT_CLOSED, p->sa);
                if (remove_peer(&(rs->peers), p) < 0)
                    return -1;
                return 0;
            }
            for (pkt = p->packets; pkt != NULL; pkt = pkt->next) {
                if (pkt->status == FAILED) {
                    handler(rs, RUDP_EVENT_TIMEOUT, p->sa);
                    if (remove_peer(&(rs->peers), p) < 0)
        return -1;
                    return 0;
                }
            }
        }
    }
    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_check_status, handler, "rudp_check_status");
    return 0;
}

/*
 * rudp_sendto: Send a block of data to the receiver.
        */

int rudp_sendto(rudp_socket_t rsocket, void* data, int len, struct sockaddr_in* to) {
    peer_t p;
    packet_t pkt;
    struct rudp_hdr *hdr;
    hdr = malloc(sizeof (struct rudp_hdr));
    if (hdr == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    pkt = malloc(sizeof (struct packet));
    if (pkt == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    p =        find_peer(&(rsocket->peers), to);
    if (p == NULL) {
        perror("rudp_sendto: find_peer");
        return -1;
    }
    if (p->status == PEER_STATUS_INIT) {
        rudp_setup_session(0, p);
    }
    hdr = malloc(sizeof (struct rudp_hdr));
    if (hdr == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    pkt = malloc(sizeof (struct packet));
    if (pkt == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    hdr->version = RUDP_VERSION;
    hdr->type = RUDP_DATA;
    hdr->seqno = p->sn++;
    pkt->data = malloc(len);
    if (pkt->data == NULL) {
        perror("rudp_sendto: malloc");
        return -1;
    }
    memcpy(pkt->data, data, len);
    pkt->datalen = len;
    pkt->header = hdr;
    pkt->next = NULL;
    pkt->trans_count = 0;
        pkt->status = IN_QUEUE;
    //printf("adding packet %d to %d\n", pkt->header->seqno, ntohs(p->sa->sin_port));
    if (add_packet(&p->packets, &pkt) < 0) {
        perror("rudp_sendto: add_packet");
        return -1;
    }
    return 0;
}

int rudp_setup_session(int fd, void *arg) {
    event_timeout_delete(rudp_setup_session, arg);
    peer_t p = (peer_t) arg;
    if (p == NULL) {
        perror("rudp_setup_session: error finding peer");
        return -1;
    }
    int sockfd;
    rudp_socket_t rs;
    peer_t pp;
    int flag = 0;
    for (rs = rs_head; rs != NULL; rs = rs->next) {
        for (pp = rs->peers; pp != NULL; pp = pp->next) {
            if (pp == p) {
                sockfd = rs->sockfd;
                flag = 1;
                break;
            }
        }
        if (flag == 1)
        break;
    }
    struct rudp_hdr header;
    header.seqno = p->sn++;
    header.type = RUDP_SYN;
    header.version = RUDP_VERSION;
    sendto(sockfd, &header, sizeof (struct rudp_hdr), 0,
            (struct sockaddr*) p->sa, sizeof (struct sockaddr_in));
    p->status = PEER_STATUS_PASSIVE;

    struct timeval now, interval, t;
    gettimeofday(&now, NULL);
    if (RUDP_TIMEOUT >= 1000) {
        interval.tv_sec        = RUDP_TIMEOUT / 1000;
        interval.tv_usec = 0;
    } else {
        interval.tv_sec = 0;
        interval.tv_usec = RUDP_TIMEOUT * 1000;
    }
    timeradd(&now, &interval, &t);
    event_timeout(t, rudp_session_check, arg, "rudp_timeout");


    gettimeofday(&now, NULL);
    interval.tv_sec = 0;
    interval.tv_usec = RUDP_CHECK_INTERVAL;
    timeradd(&now, &interval, &t);
    event_timeout(t,        rudp_send, NULL, "rudp_send");
    return 0;
}

int rudp_session_check(int fd, void *arg) {
    event_timeout_delete(rudp_session_check, arg);
    peer_t p = (peer_t) arg;
    if (p == NULL) {
        perror("rudp_setup_session: error finding peer");
        return -1;
    }
    if (p->status == PEER_STATUS_PASSIVE) {
        struct timeval now, interval, t;
        gettimeofday(&now, NULL);
        interval.tv_usec = 0;
        interval.tv_sec = 0;
        timeradd(&now, &interval, &t);
        event_timeout(t, rudp_setup_session, arg, "rudp_setup_session");
    }
    return 0;
}
Leave a Comment

Multiple Sessions

A linked list of all RUDP sockets is maintained. When rudp_socket() is called, an RUDP socket is created and added to the linked list. An RUDP socket keeps a record of the pees/sessions it talks with. When RUDP receives a packet from an unknown socket address, or when RUDP receives a send packet request to an unknown socket address, a new session is created. And for each session, a linked list of all buffered packets is kept.

Leave a Comment

Session Establishment and Tearing Down

When rudp_sendto() is called, the protocol first check if there exists a session between the sender and receiver. If not, the protocol will try to setup a session by sending RUDP_SYN messages. And the packet the application wants to send will be buffered in the created session. After an RUDP_ACK message is received, the server side socket start sending out packets. Go back N protocol is used to control the sending process. After the protocol receives a rudp_close() signal, it will first check whether there are still active sessions and packets in the sending buffer. If not, the protocol will send out RUDP_FIN messages and after receiving RUDP_ACKs, the session is torn down.

Leave a Comment

RUDP Overview

RUDP is a protocol that ensures transfer reliability with UDP. A sliding window protocol (Go back N) is used to realize reliability. Using RUDP, applications can send and receive data packets without worrying about lost packets.

The lines in red signifies state change for RUDP clients (receiver side); while the black lines signifies state change for RUDP servers (sender side).

Leave a Comment

Transferring Voice Data

This is done using JMF (Java Media Framework). Voice data is read from a file using `MediaLocator’. It is used as the data source for a `Processor’. The `Processor’ specifies and converts the audio data to a certain format and then output the converted data into a `DataSink’. The `DataSink’ then transfers the stream to its destination address and port.

Leave a Comment

Multiple Sessions

A SessionManager' is used to manage all the sessions. The SessionManager’ keeps a list of sessions. When it receives data, it will first check which session the data belongs to. Then the data is given to the corresponding session. If it does not belong to any session in the list, a new session will be created and added to the session list.

The basic code looks like this:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Daoyuan
 */
public class SessionManager extends Thread {

String user;
    String host;
    int port;
    String voiceFile;
    String defaultVoice;
    boolean listening = true;
    public static ArrayList sessionList;
    DatagramSocket serverSocket = null;

    public SessionManager(String user, String host, int port, String voiceFile,
            String defaultVoice) {
        this.user = user;
        this.host = host;
        this.port = port;
        this.voiceFile = voiceFile;
this.defaultVoice = defaultVoice;
        sessionList = new ArrayList();
        try {
            serverSocket = new DatagramSocket(port);
        } catch (SocketException ex) {
            Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("Unable to listen on port " + port);
        }
    }

    @Override
    public void run() {

        byte[] receiveData = newbyte[2048];

        new Thread() {

            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException ex) {
                        Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    if (sessionList.size() != 0) {
                        for (int i= 0; i < sessionList.size(); i++) {
                            if (sessionList.get(i).status.equals("destroyed")) {
                                sessionList.remove(i);
                            }
                        }
                    }
                }
            }
        }.start();

        while (listening) {
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
                serverSocket.receive(receivePacket);
            } catch (IOException ex) {
                Logger.getLogger(SessionManager.class.getName()).log(Level.SEVERE, null, ex);
            }

            if (isNewSession(receivePacket)) {
                System.out.println("New session created!");
            }
        }

        serverSocket.close();
    }

    public void stopListening() {
        listening = false;
}

    private boolean isNewSession(DatagramPacket receivePacket) {
        InetAddress IPAddress = receivePacket.getAddress();
        int remotePort = receivePacket.getPort();
        String sessionName = IPAddress + ":" + remotePort;
        if (sessionList.size() == 0) {
            Session s = new Session(this.serverSocket, IPAddress, remotePort, voiceFile, defaultVoice, user);
            s.setName(sessionName);
s.requests.add(receivePacket);
            sessionList.add(s);
            s.start();
            return true;
        } else {
            for (int i = 0; i < sessionList.size(); i++) {
                if (sessionList.get(i).getName().equals(sessionName)) {
                    sessionList.get(i).requests.add(receivePacket);
                    return false;
                }
            }

            Session s = newSession(this.serverSocket, IPAddress, remotePort, voiceFile, defaultVoice, user);
            s.setName(sessionName);
            s.requests.add(receivePacket);
            sessionList.add(s);
            s.start();
            return true;
        }
    }
}
Leave a Comment

Session Establishment and Tearing Down

Each session has a ‘status’, it can be ‘new’, ‘establishing’, ‘cancelling’, ‘established’, ‘tearingdown’ and ‘destroyed’.

When a new session is created, its status is ‘new’.

When an ‘INVITE’ is received, it sends out an ‘OK’ message and change its status to ‘establishing’.

After receiving an ‘ACK’ message the status will be changed to ‘established’.

Then begins the transferring of voice data using RTP.

When the sending finishes the status will become ‘tearingdown’.

A ‘BYE’ message is also sent to the client.

The status becomes ‘destroyed’ after getting ‘OK’ from the client.

When a ‘CANCEL’ message is received, the status becomes ‘cancelling’.

Then it sends back ‘OK’ and ‘Request Terminated’ messages.

After received an ‘ACK’, the status becomes ‘destroyed’.

The thread is as follows:


@Override
public void run() {

    while (true) {
        if (requests.size() == 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException ex) {
                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
            }
        } else {
            final DatagramPacket packet = requests.get(0);
            final String request = new String(packet.getData());
            System.err.println(request);
            sipRequest = new SIPRequest(request);
            if (request.toUpperCase().startsWith("INVITE")) {
                System.out.println("Got INVITE");
                if (!status.equals("new")) {
                    requests.remove(0);
                    continue;
                }

                String requestLine = request.substring(6).trim();
                String uri = requestLine.substring(0, requestLine.indexOf(' '));
                //System.out.println(uri);
                String usr = "";
                if (uri.toUpperCase().startsWith("SIP:")) {
                    usr = uri.substring(4, uri.indexOf('@'));
                } else {
                    usr = uri.substring(0, uri.indexOf('@'));
                }
                if (!usr.equalsIgnoreCase(user)) {
                    sendUserNotFound(sipRequest);
                    status = "tearingdown";
                    System.out.println("Status: " + status);
                    continue;
                }

                sendTrying(sipRequest);
                sendRinging(sipRequest);
                final String rtpport = request.substring(request.indexOf("m=audio") + 8, request.indexOf("m=audio") + 13);
                final String sdp = getSDP(request);
                new Thread() {

                    @Override
                    public void run() {
                        File file = new File("wav/" + voiceFile);
                        if (file.exists()) {
                            locator = new MediaLocator("file:/" + file.getAbsolutePath());
                        } else {
                            System.out.println("Using default voice file!");
                            locator = new MediaLocator("file:/" + file.getAbsolutePath());
                        }
                        transmitter = new Transmitter(locator, packet.getAddress().toString().substring(1), rtpport);
                        transmitter.prepare();
                    }
                }.start();

                new Thread() {

                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException ex) {
                            Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        if (status.equals("establishing")) {
                            sendOK(sipRequest, sdp, rtpport);
                        } else {
                            try {
                                transmitter.stop();
                            } catch (NullPointerException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                                System.out.println("Transmitter already stopped!");
                            }
                        }
                    }
                }.start();
                requests.remove(0);
                status = "establishing";
                System.out.println("Status: " + status);
            } else if (request.toUpperCase().startsWith("ACK")) {
                System.out.println("Got ACK");
                //sendOK(sipRequest);
                requests.remove(0);
                if (status.equals("establishing")) {
                    status = "established";
                    System.out.println("Status: " + status);
                    new Thread() {

                        @Override
                        public void run() {
                            transmitter.start();

                            long duration = new VoiceInfo(locator).getDuration();
                            try {
                                Thread.sleep(duration);
                            } catch (InterruptedException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                            }

                            try {
                                transmitter.stop();
                            } catch (NullPointerException ex) {
                                Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                                System.out.println("Transmitter already stopped!");
                            }
                            if (!status.equals("tearingdown") && !status.equals("destroyed")) {
                                sendBye(sipRequest);
                                status = "tearingdown";
                                System.out.println("Status: " + status);
                            }
                        }
                    }.start();
                } else if (status.equals("tearingdown")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                } else if (status.equals("cancelling")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                }
            } else if (request.toUpperCase().startsWith("BYE")) {
                System.out.println("Got BYE");
                sendOKWithoutSDP(sipRequest);
                requests.remove(0);
                try {
                    transmitter.stop();
                } catch (NullPointerException ex) {
                    Logger.getLogger(Session.class.getName()).log(Level.SEVERE, null, ex);
                    System.out.println("Transmitter already stopped!");
                }
                status = "destroyed";
                System.out.println("Status: " + status);
                break;
            } else if (request.toUpperCase().startsWith("SIP/2.0 200")) {
                requests.remove(0);
                System.out.println("Got OK");
                if (status.equals("tearingdown")) {
                    status = "destroyed";
                    System.out.println("Status: " + status);
                    break;
                }
            } else if (request.toUpperCase().startsWith("CANCEL")) {
                requests.remove(0);
                System.out.println("Got CANCEL");
                status = "cancelling";
                System.out.println("Status: " + status);
                sendOKWithoutSDP(sipRequest);
                sendTerminated(sipRequest);
            } else {
                requests.remove(0);
            }
        }
    }
}
Leave a Comment