1296 lines
29 KiB
C
1296 lines
29 KiB
C
#define _POSIX_C_SOURCE 200112L
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#define __USE_XOPEN_EXTENDED
|
|
#define __USE_MISC
|
|
#include <arpa/inet.h>
|
|
#include <time.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/if.h>
|
|
#include <netdb.h>
|
|
#include <netinet/in.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/udp.h>
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <getopt.h>
|
|
#include <sys/mman.h>
|
|
|
|
#define PPPLCP 0xc021
|
|
#define PPPPAP 0xc023
|
|
#define PPPCHAP 0xc223
|
|
#define PPPIPCP 0x8021
|
|
#define PPPIP 0x0021
|
|
#define PPPCCP 0x80fd
|
|
|
|
#define CONFREQ 1
|
|
#define CONFACK 2
|
|
#define CONFNAK 3
|
|
#define CONFREJ 4
|
|
#define TERMREQ 5
|
|
#define TERMACK 6
|
|
#define CODEREJ 7
|
|
#define PROTREJ 8
|
|
#define ECHOREQ 9
|
|
#define ECHOREP 10
|
|
#define DISCREQ 11
|
|
|
|
#define PACKET_LENGTH 1000
|
|
#define TARGET_PPS 5000
|
|
#define TARGET "211.29.131.33"
|
|
#define GWADDR "211.29.131.30"
|
|
#define NUM_SESSIONS 1
|
|
#define MAX_PACKETS 0
|
|
#define AVG_SIZE 5
|
|
|
|
typedef unsigned short u16;
|
|
typedef unsigned int u32;
|
|
typedef unsigned char u8;
|
|
|
|
char *lcp_codes[] = {
|
|
"reserved",
|
|
"CONFREQ",
|
|
"CONFACK",
|
|
"CONFNAK",
|
|
"CONFREJ",
|
|
"TERMREQ",
|
|
"TERMACK",
|
|
"CODEREJ",
|
|
"PROTREJ",
|
|
"ECHOREQ",
|
|
"ECHOREP",
|
|
"DISCREQ",
|
|
};
|
|
|
|
char *mtypes[] = {
|
|
"reserved",
|
|
"SCCRQ",
|
|
"SCCRP",
|
|
"SCCCN",
|
|
"StopCCN", // 4
|
|
"reserved",
|
|
"HELLO",
|
|
"OCRQ",
|
|
"OCRP",
|
|
"OCCN",
|
|
"ICRQ", // 10
|
|
"ICRP",
|
|
"ICCN",
|
|
"reserved",
|
|
"CDN",
|
|
"WEN", // 15
|
|
"SLI",
|
|
};
|
|
|
|
char *attributes[] = {
|
|
"Message Type", // 0
|
|
"Result Code", // 1
|
|
"Protocol Version", // 2
|
|
"Framing Capabilities", // 3
|
|
"Bearer Capabilities", // 4
|
|
"Tie Breaker", // 5
|
|
"Firmware Revision", // 6
|
|
"Host Name", // 7
|
|
"Vendor Name", // 8
|
|
"Assigned Tunnel ID", // 9
|
|
"Receive Window Size", // 10
|
|
"Challenge", // 11
|
|
"Q.931 Cause Code", // 12
|
|
"Challenge Response", // 13
|
|
"Assigned Session ID", // 14
|
|
"Call Serial Number", // 15
|
|
"Minimum BPS", // 16
|
|
"Maximum BPS", // 17
|
|
"Bearer Type", // 18 (2 = Analog, 1 = Digital)
|
|
"Framing Type", // 19 (2 = Async, 1 = Sync)
|
|
"Reserved 20", // 20
|
|
"Called Number", // 21
|
|
"Calling Number", // 22
|
|
"Sub Address", // 23
|
|
"Tx Connect Speed", // 24
|
|
"Physical Channel ID", // 25
|
|
"Initial Received LCP CONFREQ", // 26
|
|
"Last Sent LCP CONFREQ", // 27
|
|
"Last Received LCP CONFREQ", // 28
|
|
"Proxy Authen Type", // 29
|
|
"Proxy Authen Name", // 30
|
|
"Proxy Authen Challenge", // 31
|
|
"Proxy Authen ID", // 32
|
|
"Proxy Authen Response", // 33
|
|
"Call Errors", // 34
|
|
"ACCM", // 35
|
|
"Random Vector", // 36
|
|
"Private Group ID", // 37
|
|
"Rx Connect Speed", // 38
|
|
"Sequencing Required", // 39
|
|
};
|
|
|
|
char *result_codes[] = {
|
|
"Reserved",
|
|
"General request to clear control connection",
|
|
"General error--Error Code indicates the problem",
|
|
"Control channel already exists",
|
|
"Requester is not authorized to establish a control channel",
|
|
"The protocol version of the requester is not supported",
|
|
"Requester is being shut down",
|
|
"Finite State Machine error",
|
|
};
|
|
|
|
char *error_codes[] = {
|
|
"No general error",
|
|
"No control connection exists yet for this LAC-LNS pair",
|
|
"Length is wrong",
|
|
"One of the field values was out of range or reserved field was non-zero",
|
|
"Insufficient resources to handle this operation now",
|
|
"The Session ID is invalid in this context",
|
|
"A generic vendor-specific error occurred in the LAC",
|
|
"Try another LNS",
|
|
"Session or tunnel was shutdown due to receipt of an unknown AVP with the M-bit set",
|
|
};
|
|
|
|
|
|
typedef struct
|
|
{
|
|
char buf[4096];
|
|
int length;
|
|
} controlt;
|
|
|
|
typedef struct avp_s
|
|
{
|
|
int length;
|
|
int type;
|
|
struct avp_s *next;
|
|
char value[1024];
|
|
} avp;
|
|
|
|
typedef struct
|
|
{
|
|
int length;
|
|
u16 session;
|
|
u16 tunnel;
|
|
u16 ns;
|
|
u16 nr;
|
|
u16 mtype;
|
|
char *buf;
|
|
avp *first;
|
|
avp *last;
|
|
} control_message;
|
|
|
|
typedef struct {
|
|
long long send_count, recv_count;
|
|
long long spkt, rpkt ;
|
|
int dropped;
|
|
long sbytes, rbytes ;
|
|
int quitit;
|
|
struct sessiont
|
|
{
|
|
short remote_session;
|
|
char open;
|
|
int ppp_state;
|
|
unsigned char ppp_identifier;
|
|
int addr;
|
|
} sessions[65536];
|
|
|
|
int active_sessions ;
|
|
} sharedt;
|
|
|
|
sharedt * ss;
|
|
|
|
void controlsend(controlt * c, short t, short s);
|
|
void controlnull(short t);
|
|
controlt *controlnew(u16 mtype);
|
|
void controls(controlt * c, u16 avp, char *val, u8 m);
|
|
void control16(controlt * c, u16 avp, u16 val, u8 m);
|
|
void control32(controlt * c, u16 avp, u32 val, u8 m);
|
|
void controlfree(controlt *c);
|
|
control_message *parsecontrol(char *buf, int length);
|
|
void dump_control_message(control_message *c);
|
|
u32 avp_get_32(control_message *c, int id);
|
|
u16 avp_get_16(control_message *c, int id);
|
|
char *avp_get_s(control_message *c, int id);
|
|
void reader_thread(void);
|
|
void skip_zlb();
|
|
void cm_free(control_message *m);
|
|
controlt *ppp_new(u16 session, int protocol);
|
|
void ppp_free(controlt *packet);
|
|
controlt *ppp_lcp(u16 s, unsigned char type, char identifier);
|
|
controlt *ppp_ipcp(u16 s, unsigned char type, char identifier);
|
|
void ppp_send(controlt *c);
|
|
void ppp_add_16(controlt * c, u16 val);
|
|
void ppp_add_32(controlt * c, u32 val);
|
|
void ppp_add_s(controlt * c, char *val);
|
|
void ppp_lcp_add_option(controlt *c, unsigned char option, unsigned char length, int data);
|
|
void dump_ppp_packet(char *packet, int l);
|
|
controlt *ppp_pap(u16 s, unsigned char type, char identifier, char *username, char *password);
|
|
char *inet_toa(unsigned long addr);
|
|
__u16 checksum(unsigned char *addr, int count);
|
|
void sigalarm(int junk);
|
|
void sigint(int signal);
|
|
void clean_shutdown();
|
|
void print_report();
|
|
|
|
int ns = 0, nr = 0;
|
|
int udpfd;
|
|
int t = 0;
|
|
struct sockaddr_in gatewayaddr;
|
|
int numsessions = NUM_SESSIONS;
|
|
int packet_length = PACKET_LENGTH;
|
|
int target_pps = TARGET_PPS;
|
|
char *target = TARGET;
|
|
char *gwaddr = GWADDR;
|
|
int max_packets = MAX_PACKETS;
|
|
int ppsend;
|
|
int do_init = 1;
|
|
char **session_usernames;
|
|
char *base_username = "dslloadtest";
|
|
char *base_password = "testing";
|
|
char *suffix = "@optusnet.com.au";
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int s;
|
|
unsigned char *packet;
|
|
|
|
ss = (sharedt*) mmap(NULL, sizeof(*ss), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
|
|
|
|
// Process Arguments {{{
|
|
while ((s = getopt(argc, argv, "?hs:g:l:p:m:t:nU:P:")) > 0)
|
|
{
|
|
switch (s)
|
|
{
|
|
case 's' :
|
|
numsessions = atoi(optarg);
|
|
if (numsessions <= 0)
|
|
{
|
|
printf("You must have at least 1 session\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 'l' :
|
|
packet_length = atoi(optarg);
|
|
if (packet_length < 64)
|
|
{
|
|
printf("You must have at least 64 byte packets\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 'n' :
|
|
do_init = 0;
|
|
break;
|
|
case 'p' :
|
|
target_pps = atoi(optarg);
|
|
break;
|
|
case 'm' :
|
|
max_packets = atoi(optarg);
|
|
if (max_packets < 50)
|
|
{
|
|
printf("You must send at least 50 packets.\n");
|
|
return -1;
|
|
}
|
|
break;
|
|
case 't' :
|
|
target = strdup(optarg);
|
|
break;
|
|
case 'g' :
|
|
gwaddr = strdup(optarg);
|
|
break;
|
|
case 'U' :
|
|
base_username = strdup(optarg);
|
|
break;
|
|
case 'P' :
|
|
base_password = strdup(optarg);
|
|
break;
|
|
case 'h' :
|
|
case '?' :
|
|
printf("Options:\n");
|
|
printf("\t-s number of ss->sessions\n");
|
|
printf("\t-l packet length\n");
|
|
printf("\t-p target pps\n");
|
|
printf("\t-m maximum number of packets\n");
|
|
printf("\t-t target IP address\n");
|
|
printf("\t-g gateway IP address\n");
|
|
printf("\t-U username (or base if multiple)\n");
|
|
printf("\t-P password\n");
|
|
return(0);
|
|
break;
|
|
}
|
|
}
|
|
if (target_pps)
|
|
ppsend = target_pps / 50;
|
|
else
|
|
ppsend = 0;
|
|
|
|
packet = calloc(4096, 1);
|
|
|
|
memset(ss->sessions, 0, sizeof(ss->sessions));
|
|
|
|
if (do_init)
|
|
printf("Creating %d ss->sessions to %s\n", numsessions, gwaddr);
|
|
printf("Targeting %d packets per second\n", target_pps);
|
|
if (max_packets) printf("Sending a maximum of %d packets\n", max_packets);
|
|
printf("Sending packets to %s\n", target);
|
|
printf("Sending %d byte packets\n", packet_length);
|
|
|
|
session_usernames = (char **)calloc(sizeof(char *), numsessions);
|
|
if (numsessions > 1)
|
|
{
|
|
int sul = strlen(base_username) + 10;
|
|
int i;
|
|
|
|
for (i = 0; i < numsessions; i++)
|
|
{
|
|
session_usernames[i] = (char *)calloc(sul, 1);
|
|
snprintf(session_usernames[i], sul, "%s%d", base_username, i+1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
session_usernames[0] = strdup(base_username);
|
|
}
|
|
// }}}
|
|
|
|
// Create socket/*{{{*/
|
|
{
|
|
int on = 1;
|
|
struct sockaddr_in addr;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(38001);
|
|
|
|
udpfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (udpfd <= 0)
|
|
{
|
|
perror("socket");
|
|
return -1;
|
|
}
|
|
|
|
setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
|
if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0)
|
|
{
|
|
perror("bind");
|
|
return -1;
|
|
}
|
|
|
|
printf("Bound to port %d\n", htons(addr.sin_port));
|
|
}/*}}}*/
|
|
|
|
memset(&gatewayaddr, 0, sizeof(gatewayaddr));
|
|
gatewayaddr.sin_family = AF_INET;
|
|
gatewayaddr.sin_port = htons(1701);
|
|
inet_aton(gwaddr, &gatewayaddr.sin_addr);
|
|
|
|
// Create tunnel/*{{{*/
|
|
if (do_init) {
|
|
controlt *c;
|
|
control_message *r;
|
|
|
|
c = controlnew(1); // SCCRQ
|
|
controls(c, 7, "loadtest", 0); // Tunnel Hostname
|
|
controls(c, 8, "OIE", 0); // Vendor Name
|
|
control16(c, 9, 1, 0); // Assigned Tunnel ID
|
|
control16(c, 2, 256, 0); // Version 1.0
|
|
control16(c, 3, 1, 0); // Framing (Async)
|
|
control16(c, 4, 1, 0); // Bearer (Digital)
|
|
control16(c, 10, 20, 0); // Receive Window Size
|
|
controlsend(c, 0, 0);
|
|
controlfree(c);
|
|
|
|
// Receive reply/*{{{*/
|
|
{
|
|
struct sockaddr_in addr;
|
|
int l;
|
|
socklen_t alen = sizeof(addr);
|
|
|
|
l = recvfrom(udpfd, packet, 4096, 0, (void *) &addr, &alen);
|
|
if (l < 0)
|
|
{
|
|
printf("Error creating tunnel: %s\n", strerror(errno));
|
|
return -1;
|
|
}
|
|
printf("Received ");
|
|
r = parsecontrol((char *) packet, l);
|
|
if (!r->first)
|
|
{
|
|
printf("Invalid packet.. no first avp\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Assigned tunnel: %d\n", t = avp_get_16(r, 9));
|
|
cm_free(r);
|
|
|
|
c = controlnew(3); // SCCCN
|
|
controlsend(c, t, 0);
|
|
controlfree(c);
|
|
skip_zlb();
|
|
}/*}}}*/
|
|
}/*}}}*/
|
|
|
|
|
|
// Create ss->sessions/*{{{*/
|
|
if (do_init)
|
|
{
|
|
for (s = 1; s <= numsessions; s++)
|
|
{
|
|
controlt *c;
|
|
|
|
c = controlnew(10); // ICRQ
|
|
controls(c, 21, "12356", 0); // Called Number
|
|
controls(c, 22, "000", 0); // Calling Number
|
|
control16(c, 14, s, 0); // Assigned Session ID
|
|
controlsend(c, t, 0);
|
|
controlfree(c);
|
|
usleep(15000); // 15 ms
|
|
}
|
|
}
|
|
printf("All session create requests sent...\n");/*}}}*/
|
|
|
|
if ( fork() == 0) {
|
|
reader_thread();
|
|
exit(0);
|
|
}
|
|
|
|
{
|
|
char tmp[512];
|
|
fprintf(stderr, "Press enter to begin sending traffic\n");
|
|
fgets(tmp, 512, stdin);
|
|
}
|
|
|
|
fprintf(stderr, "Beginning sending traffic through %d ss->sessions\n", ss->active_sessions);
|
|
printf(" TS: Total Packets Sent\n");
|
|
printf(" TL: Total Packets Lost\n");
|
|
printf(" PL: Packet Loss\n");
|
|
printf(" SS: Send Speed\n");
|
|
printf(" RS: Receive Speed\n");
|
|
printf(" SP: Packets/Second Sent\n");
|
|
printf(" RP: Packets/Second Received\n");
|
|
printf(" NS: Number of active ss->sessions\n");
|
|
|
|
signal(SIGALRM, sigalarm);
|
|
signal(SIGINT, sigint);
|
|
alarm(1);
|
|
|
|
// Traffic generation loop {{{
|
|
{
|
|
struct sockaddr_in to;
|
|
struct iphdr *iph;
|
|
struct udphdr *udph;
|
|
char *data;
|
|
int len = 0;
|
|
unsigned int seq = 0;
|
|
controlt *c;
|
|
|
|
// Get address
|
|
memset(&to, 0, sizeof(struct sockaddr_in));
|
|
to.sin_family = AF_INET;
|
|
inet_aton(target, &to.sin_addr);
|
|
|
|
c = ppp_new(1, PPPIP);
|
|
|
|
iph = (struct iphdr *)(c->buf + c->length);
|
|
udph = (struct udphdr *)(c->buf + c->length + sizeof(struct iphdr));
|
|
data = (char *)(c->buf + c->length + sizeof(struct iphdr) + sizeof(struct udphdr));
|
|
len = sizeof(struct iphdr) + sizeof(struct udphdr);
|
|
c->length += len;
|
|
|
|
//IP
|
|
c->length += sizeof(struct iphdr);
|
|
iph->tos = 0;
|
|
iph->id = ntohs(1);
|
|
iph->frag_off = ntohs(1 << 14);
|
|
iph->ttl = 30;
|
|
iph->check = 0;
|
|
iph->version = 4;
|
|
iph->ihl = 5;
|
|
iph->protocol = 17;
|
|
memcpy(&iph->daddr, &to.sin_addr, sizeof(iph->daddr));
|
|
|
|
// UDP
|
|
udph->source = ntohs(39999);
|
|
udph->dest = ntohs(39000);
|
|
udph->check = 0;
|
|
|
|
// Data
|
|
memset(data, 64, 1500);
|
|
|
|
udph->len = ntohs(sizeof(struct udphdr) + packet_length);
|
|
iph->tot_len = ntohs(len + packet_length);
|
|
c->length += packet_length;
|
|
|
|
while (!ss->quitit && ss->active_sessions)
|
|
{
|
|
int i;
|
|
for (i = 1; i <= numsessions && !ss->quitit; i++)
|
|
{
|
|
// Skip ss->sessions that aren't active yet
|
|
if (!ss->sessions[i].open || ss->sessions[i].ppp_state != 2)
|
|
continue;
|
|
|
|
*(u16 *)(c->buf + 4) = htons(ss->sessions[i].remote_session); // Session ID
|
|
iph->saddr = ss->sessions[i].addr;
|
|
iph->check = 0;
|
|
iph->check = ntohs(checksum((unsigned char *)iph, sizeof(struct iphdr)));
|
|
|
|
*((unsigned int *) data) = seq++;
|
|
ppp_send(c);
|
|
|
|
ss->send_count++;
|
|
ss->spkt++;
|
|
ss->sbytes += c->length;
|
|
|
|
if (ppsend && ss->send_count % ppsend == 0)
|
|
{
|
|
struct timespec req;
|
|
req.tv_sec = 0;
|
|
req.tv_nsec = 5 * 1000 * 1000;
|
|
nanosleep(&req, NULL);
|
|
}
|
|
|
|
if (max_packets && ss->send_count >= max_packets)
|
|
ss->quitit++;
|
|
}
|
|
}
|
|
|
|
c->length -= packet_length;
|
|
|
|
}/*}}}*/
|
|
|
|
clean_shutdown();
|
|
print_report();
|
|
|
|
close(udpfd);
|
|
return 0;
|
|
}
|
|
|
|
void print_report()
|
|
{
|
|
float loss;
|
|
|
|
loss = 100 - (((ss->recv_count * 1.0) / (ss->send_count * 1.0)) * 100.0);
|
|
|
|
printf("\n");
|
|
printf("Total Packets Sent: %llu\n", ss->send_count);
|
|
printf("Total Packets Received: %llu\n", ss->recv_count);
|
|
printf("Overall Packet Loss: %0.2f%%", loss);
|
|
printf("\n");
|
|
}
|
|
|
|
void clean_shutdown()/*{{{*/
|
|
{
|
|
int i;
|
|
for (i = 0; i < numsessions; i++)
|
|
{
|
|
// Close Session
|
|
controlt *c;
|
|
|
|
if (!ss->sessions[i].open) continue;
|
|
c = controlnew(14); // CDN
|
|
control16(c, 14, i, 0); // Assigned Session ID
|
|
control16(c, 1, 1, 0); // Result Code
|
|
controlsend(c, t, ss->sessions[i].remote_session);
|
|
controlfree(c);
|
|
}
|
|
|
|
// Close Tunnel
|
|
{
|
|
controlt *c;
|
|
|
|
c = controlnew(4); // StopCCN
|
|
control16(c, 9, 1, 0); // Assigned Tunnel ID
|
|
control16(c, 1, 1, 0); // Result Code
|
|
controlsend(c, t, 0);
|
|
controlfree(c);
|
|
}
|
|
}/*}}}*/
|
|
|
|
void sigint(int unused __attribute__ ((unused)))
|
|
{
|
|
ss->quitit++;
|
|
}
|
|
|
|
void sigalarm(int unused __attribute__ ((unused)))
|
|
{
|
|
static unsigned long long last_rpkts[AVG_SIZE], last_spkts[AVG_SIZE];
|
|
static int last = 0, avg_count = 0;
|
|
unsigned int avg_s = 0, avg_r = 0;
|
|
int i;
|
|
float loss;
|
|
|
|
last_rpkts[last] = ss->rpkt;
|
|
last_spkts[last] = ss->spkt;
|
|
last = (last + 1) % AVG_SIZE;
|
|
if (avg_count < AVG_SIZE)
|
|
avg_count++;
|
|
|
|
for (i = 0; i < avg_count; i++)
|
|
{
|
|
avg_s += last_spkts[i];
|
|
avg_r += last_rpkts[i];
|
|
}
|
|
avg_s /= avg_count;
|
|
avg_r /= avg_count;
|
|
|
|
loss = 100 - (((avg_r * 1.0) / (avg_s * 1.0)) * 100.0);
|
|
fprintf(stderr, "TS:%llu TL:%lld DR:%4d PL:%-3.2f%% SS:%0.1fMbits/s RS:%0.1fMbits/s NS:%u SP:%u RP:%u\n",
|
|
ss->send_count, ss->send_count-ss->recv_count, ss->dropped, loss,
|
|
(ss->sbytes/1024.0/1024.0*8), (ss->rbytes/1024.0/1024.0*8),
|
|
ss->active_sessions,
|
|
avg_s, avg_r);
|
|
|
|
ss->spkt = ss->rpkt = 0;
|
|
ss->sbytes = ss->rbytes = 0;
|
|
alarm(1);
|
|
}
|
|
|
|
__u16 checksum(unsigned char *addr, int count)
|
|
{
|
|
register long sum = 0;
|
|
|
|
for (; count > 1; count -= 2)
|
|
{
|
|
sum += ntohs(*(u16 *)addr);
|
|
addr += 2;
|
|
}
|
|
|
|
if (count > 0) sum += *(unsigned char *)addr;
|
|
|
|
// take only 16 bits out of the 32 bit sum and add up the carries
|
|
if (sum >> 16)
|
|
sum = (sum & 0xFFFF) + (sum >> 16);
|
|
|
|
// one's complement the result
|
|
sum = ~sum;
|
|
|
|
return ((u16) sum);
|
|
}
|
|
|
|
// Control Stuff {{{
|
|
void control16(controlt * c, u16 avp, u16 val, u8 m)
|
|
{
|
|
u16 l = (m ? 0x8008 : 0x0008);
|
|
*(u16 *) (c->buf + c->length + 0) = htons(l);
|
|
*(u16 *) (c->buf + c->length + 2) = htons(0);
|
|
*(u16 *) (c->buf + c->length + 4) = htons(avp);
|
|
*(u16 *) (c->buf + c->length + 6) = htons(val);
|
|
c->length += 8;
|
|
}
|
|
|
|
// add an AVP (32 bit)
|
|
void control32(controlt * c, u16 avp, u32 val, u8 m)
|
|
{
|
|
u16 l = (m ? 0x800A : 0x000A);
|
|
*(u16 *) (c->buf + c->length + 0) = htons(l);
|
|
*(u16 *) (c->buf + c->length + 2) = htons(0);
|
|
*(u16 *) (c->buf + c->length + 4) = htons(avp);
|
|
*(u32 *) (c->buf + c->length + 6) = htonl(val);
|
|
c->length += 10;
|
|
}
|
|
|
|
// add an AVP (32 bit)
|
|
void controls(controlt * c, u16 avp, char *val, u8 m)
|
|
{
|
|
u16 l = ((m ? 0x8000 : 0) + strlen(val) + 6);
|
|
*(u16 *) (c->buf + c->length + 0) = htons(l);
|
|
*(u16 *) (c->buf + c->length + 2) = htons(0);
|
|
*(u16 *) (c->buf + c->length + 4) = htons(avp);
|
|
memcpy(c->buf + c->length + 6, val, strlen(val));
|
|
c->length += 6 + strlen(val);
|
|
}
|
|
|
|
// new control connection
|
|
controlt *controlnew(u16 mtype)
|
|
{
|
|
controlt *c;
|
|
c = calloc(sizeof(controlt), 1);
|
|
c->length = 12;
|
|
control16(c, 0, mtype, 1);
|
|
return c;
|
|
}
|
|
|
|
void controlnull(short t)
|
|
{
|
|
controlt *c;
|
|
c = calloc(sizeof(controlt), 1);
|
|
c->length = 12;
|
|
controlsend(c, t, 0);
|
|
controlfree(c);
|
|
ns--;
|
|
}
|
|
|
|
// add a control message to a tunnel, and send if within window
|
|
void controlsend(controlt * c, short t, short s)
|
|
{
|
|
*(u16 *) (c->buf + 0) = htons(0xC802); // flags/ver
|
|
*(u16 *) (c->buf + 2) = htons(c->length); // length
|
|
*(u16 *) (c->buf + 4) = htons(t); // tunnel
|
|
*(u16 *) (c->buf + 6) = htons(s); // session
|
|
*(u16 *) (c->buf + 8) = htons(ns++); // sequence
|
|
*(u16 *) (c->buf + 10) = htons(nr); // sequence
|
|
// printf("Sending ");
|
|
// cm_free(parsecontrol(c->buf, c->length));
|
|
sendto(udpfd, c->buf, c->length, 0, (struct sockaddr *)&gatewayaddr, sizeof(gatewayaddr));
|
|
}
|
|
|
|
void controlfree(controlt *c)
|
|
{
|
|
if (!c) return;
|
|
free(c);
|
|
}
|
|
|
|
control_message *parsecontrol(char *buf, int length)
|
|
{
|
|
char *p = buf;
|
|
control_message *c;
|
|
|
|
c = calloc(sizeof(control_message), 1);
|
|
c->buf = buf;
|
|
c->length = length;
|
|
|
|
c->tunnel = ntohs(*(u16 *)(buf + 4));
|
|
c->session = ntohs(*(u16 *)(buf + 6));
|
|
c->ns = ntohs(*(u16 *)(buf + 8));
|
|
c->nr = nr = ntohs(*(u16 *)(buf + 10));
|
|
p += 12;
|
|
while ((p - buf) < length)
|
|
{
|
|
avp *a = calloc(sizeof(avp), 1);
|
|
a->length = ntohs(*(short *)(p)) & 0x3FF;
|
|
a->type = ntohs(*(short *)(p + 4));
|
|
memcpy(a->value, p + 6, a->length - 6);
|
|
if (a->type == 0) c->mtype = ntohs(*(short *)a->value);
|
|
p += a->length;
|
|
if (c->last)
|
|
c->last->next = a;
|
|
else
|
|
c->first = a;
|
|
c->last = a;
|
|
}
|
|
if (c->first)
|
|
dump_control_message(c);
|
|
return c;
|
|
}
|
|
|
|
void dump_control_message(control_message *c)
|
|
{
|
|
avp *a;
|
|
printf("Control Message (type=%u s=%u t=%d ns=%d nr=%d)\n", c->mtype, c->session, c->tunnel, c->ns, c->nr);
|
|
for (a = c->first; a; a = a->next)
|
|
{
|
|
printf(" avp: %s, len: %d", attributes[a->type], a->length - 6);
|
|
switch (a->type)
|
|
{
|
|
// Short
|
|
case 6 :
|
|
case 9 :
|
|
case 10 :
|
|
case 39 :
|
|
case 14 : printf(", value: %u\n", ntohs(*(short *)a->value));
|
|
break;
|
|
|
|
// Integer
|
|
case 16 :
|
|
case 17 :
|
|
case 24 :
|
|
case 25 :
|
|
case 38 :
|
|
case 15 : printf(", value: %u\n", ntohl(*(u32 *)a->value));
|
|
break;
|
|
|
|
// String
|
|
case 7 :
|
|
case 21 :
|
|
case 22 :
|
|
case 23 :
|
|
case 37 :
|
|
case 8 : printf(", value: \"%s\"\n", a->value);
|
|
break;
|
|
|
|
case 2 : printf(", value: %d.%d\n", *(char *)a->value, *(char *)a->value + 1);
|
|
break;
|
|
case 0 : printf(", value: %s\n", mtypes[ntohs(*(short *)a->value)]);
|
|
break;
|
|
case 19 :
|
|
case 3 : printf(", value: (%d) %s %s\n", ntohl(*(u32 *)a->value),
|
|
(ntohl(*(u32 *)a->value) & 0x01) ? "synchronous" : "",
|
|
(ntohl(*(u32 *)a->value) & 0x02) ? "asynchronous" : "");
|
|
break;
|
|
case 18 :
|
|
case 4 : printf(", value: (%d) %s %s\n", ntohl(*(u32 *)a->value),
|
|
(ntohl(*(u32 *)a->value) & 0x01) ? "digital" : "",
|
|
(ntohl(*(u32 *)a->value) & 0x02) ? "analog" : "");
|
|
break;
|
|
|
|
default : printf("\n");
|
|
break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
u16 avp_get_16(control_message *c, int id)
|
|
{
|
|
avp *a;
|
|
|
|
for (a = c->first; a; a = a->next)
|
|
if (a->type == id) return ntohs(*(short *)a->value);
|
|
return 0;
|
|
}
|
|
|
|
u32 avp_get_32(control_message *c, int id)
|
|
{
|
|
avp *a;
|
|
|
|
for (a = c->first; a; a = a->next)
|
|
if (a->type == id) return ntohl(*(u32 *)a->value);
|
|
return 0;
|
|
}
|
|
|
|
char *avp_get_s(control_message *c, int id)
|
|
{
|
|
avp *a;
|
|
|
|
for (a = c->first; a; a = a->next)
|
|
if (a->type == id) return (char *)a->value;
|
|
return 0;
|
|
}
|
|
|
|
void cm_free(control_message *m)
|
|
{
|
|
avp *a, *n;
|
|
|
|
for (a = m->first; a; )
|
|
{
|
|
n = a->next;
|
|
free(a);
|
|
a = n;
|
|
}
|
|
|
|
free(m);
|
|
}
|
|
|
|
// }}}
|
|
|
|
void reader_thread()/*{{{*/
|
|
{
|
|
unsigned char *packet;
|
|
unsigned int seq = 0;
|
|
|
|
printf("Starting reader thread\n");
|
|
packet = malloc(4096);
|
|
while (!ss->quitit)
|
|
{
|
|
struct sockaddr_in addr;
|
|
socklen_t alen = sizeof(addr);
|
|
control_message *m;
|
|
int l;
|
|
int s;
|
|
int pfc = 0;
|
|
|
|
// memset(packet, 0, 4096);
|
|
if ((l = recvfrom(udpfd, packet, 4096, 0, (void *) &addr, &alen)) < 0) break;
|
|
ss->rbytes += l;
|
|
if (!do_init)
|
|
{
|
|
ss->recv_count++;
|
|
ss->rpkt++;
|
|
continue;
|
|
}
|
|
if (l < 12)
|
|
{
|
|
printf("Short packet received: %d bytes\n", l);
|
|
}
|
|
s = ntohs(*(u16 *)(packet + 4));
|
|
if (!s)
|
|
{
|
|
printf("Invalid session ID\n");
|
|
continue;
|
|
}
|
|
if (packet[0] == 0xc8)
|
|
{
|
|
// Control Packet
|
|
printf("Reader Received ");
|
|
m = parsecontrol((char *) packet, l);
|
|
printf("\n");
|
|
s = m->session;
|
|
|
|
switch (m->mtype)
|
|
{
|
|
case 4 : printf("StopCCN\n");
|
|
printf("Killing tunnel %d\n", avp_get_16(m, 9));
|
|
ss->quitit++;
|
|
break;
|
|
case 6 : printf("HELLO, sending ZLB ACK\n");
|
|
controlnull(t);
|
|
break;
|
|
case 11 :
|
|
{
|
|
controlt *c;
|
|
|
|
printf("Received ICRP. Responding with CONFREQ\n");
|
|
|
|
ss->sessions[s].remote_session = avp_get_16(m, 14);
|
|
ss->sessions[s].open = 1;
|
|
ss->sessions[s].ppp_state = 1;
|
|
|
|
c = controlnew(12); // ICCN
|
|
controlsend(c, t, ss->sessions[s].remote_session);
|
|
controlfree(c);
|
|
|
|
c = ppp_lcp(s, CONFREQ, 0);
|
|
ppp_lcp_add_option(c, 1, 2, htons(1500)); // MRU = 1400
|
|
ppp_lcp_add_option(c, 3, 2, htons(0xC023)); // Authentication Protocol - PAP
|
|
ppp_send(c);
|
|
controlfree(c);
|
|
break;
|
|
}
|
|
case 14 : {
|
|
int s;
|
|
printf("CDN\n");
|
|
s = avp_get_16(m, 14);
|
|
printf("Killing session %d\n", s);
|
|
ss->sessions[s].open = 0;
|
|
ss->sessions[s].ppp_state = 0;
|
|
ss->active_sessions--;
|
|
controlnull(t);
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (m->mtype == 4)
|
|
{
|
|
printf("StopCCN Received.. Dieing\n");
|
|
ss->quitit++;
|
|
break;
|
|
}
|
|
cm_free(m);
|
|
}
|
|
else
|
|
{
|
|
// Data Packet
|
|
unsigned short protocol = ntohs(*(u16 *)(packet + 6));
|
|
|
|
if (protocol == 0xff03)
|
|
{
|
|
pfc = 2;
|
|
packet += 2;
|
|
protocol = ntohs(*(u16 *)(packet + 6));
|
|
}
|
|
if (protocol != PPPIP)
|
|
{
|
|
printf("Received ");
|
|
dump_ppp_packet((char *) (packet + 6), l - 6);
|
|
}
|
|
|
|
if (protocol == PPPLCP)
|
|
{
|
|
controlt *r;
|
|
unsigned char ppp_id = *(char *)(packet + 9);
|
|
|
|
switch (*(char *)(packet + 8))
|
|
{
|
|
case CONFREQ :
|
|
r = ppp_lcp(s, CONFACK, ppp_id);
|
|
ppp_send(r);
|
|
break;
|
|
case CONFACK :
|
|
r = ppp_pap(s, CONFREQ, 0, session_usernames[s-1], base_password);
|
|
ppp_send(r);
|
|
break;
|
|
case TERMREQ :
|
|
r = ppp_lcp(s, TERMACK, ppp_id);
|
|
ppp_send(r);
|
|
break;
|
|
case ECHOREQ :
|
|
r = ppp_lcp(s, ECHOREP, ppp_id);
|
|
ppp_add_32(r, 0);
|
|
ppp_send(r);
|
|
break;
|
|
}
|
|
}
|
|
else if (protocol == PPPIPCP)
|
|
{
|
|
controlt *r;
|
|
int taddr = 0;
|
|
u32 address = *(u32 *)(packet + 14);
|
|
|
|
switch (*(char *)(packet + 8))
|
|
{
|
|
case CONFREQ :
|
|
r = ppp_ipcp(s, CONFREQ, time(NULL) % 255);
|
|
ppp_lcp_add_option(r, 3, 4, htonl(taddr)); // Request 0.0.0.0
|
|
ppp_send(r);
|
|
controlfree(r);
|
|
r = ppp_ipcp(s, CONFACK, time(NULL) % 255);
|
|
ppp_lcp_add_option(r, 3, 4, address); // ACK gateway IP
|
|
ppp_send(r);
|
|
controlfree(r);
|
|
break;
|
|
case CONFNAK :
|
|
// Request whatever address we are given - it's ours
|
|
r = ppp_ipcp(s, CONFREQ, time(NULL) % 255);
|
|
ppp_lcp_add_option(r, 3, 4, address);
|
|
ppp_send(r);
|
|
controlfree(r);
|
|
printf("Session %d: %s\n", s, inet_toa(address));
|
|
ss->sessions[s].ppp_state = 2;
|
|
ss->sessions[s].addr = address;
|
|
ss->active_sessions++;
|
|
break;
|
|
case CONFACK :
|
|
printf("Conf-Ack Received\n");
|
|
break;
|
|
case TERMREQ :
|
|
printf("Term-Req Received\n");
|
|
break;
|
|
case ECHOREQ :
|
|
printf("Echo-Req Received\n");
|
|
break;
|
|
case ECHOREP :
|
|
printf("Echo-Rep Received\n");
|
|
break;
|
|
}
|
|
}
|
|
else if (protocol == PPPPAP)
|
|
{
|
|
if (*(u16 *)(packet + 8) == 3)
|
|
{
|
|
controlt *c;
|
|
printf("Closing Connection\n");
|
|
|
|
c = controlnew(14); // CDN
|
|
control16(c, 14, ss->sessions[s].remote_session, 0); // Assigned Session ID
|
|
controlsend(c, t, 0);
|
|
controlfree(c);
|
|
ss->sessions[s].open = 0;
|
|
}
|
|
}
|
|
else if (protocol == PPPIP)
|
|
{
|
|
struct iphdr *iph = (struct iphdr *)(packet + 8);
|
|
char * data = (char*) (packet + 8 + sizeof(struct iphdr) + sizeof(struct udphdr));
|
|
if (!ss->sessions[s].open)
|
|
{
|
|
printf("Packet for closed session %d\n", s);
|
|
continue;
|
|
}
|
|
|
|
if (iph->protocol == 17)
|
|
{
|
|
unsigned int iseq;
|
|
ss->recv_count++;
|
|
ss->rpkt++;
|
|
iseq = *((unsigned int *) data);
|
|
if (seq != iseq)
|
|
ss->dropped += (iseq - seq) ;
|
|
|
|
seq = iseq + 1; // Next sequence number to expect.
|
|
}
|
|
}
|
|
}
|
|
packet -= pfc;
|
|
}
|
|
free(packet);
|
|
|
|
printf("Closing reader thread\n");
|
|
|
|
}/*}}}*/
|
|
|
|
void skip_zlb() /*{{{*/
|
|
{
|
|
struct sockaddr_in addr;
|
|
socklen_t alen = sizeof(addr);
|
|
char buf[1024];
|
|
int l;
|
|
l = recvfrom(udpfd, buf, 1024, MSG_PEEK, (void *) &addr, &alen);
|
|
if (l < 0)
|
|
{
|
|
printf("recvfrom: %s\n", strerror(errno));
|
|
return;
|
|
}
|
|
if (l <= 12)
|
|
{
|
|
printf("Skipping ZLB (l=%d)\n", l);
|
|
recvfrom(udpfd, buf, 1024, 0, (void *) &addr, &alen);
|
|
}
|
|
}
|
|
/*}}}*/
|
|
|
|
// PPP Stuff {{{
|
|
controlt *ppp_new(u16 session, int protocol)
|
|
{
|
|
controlt *c = calloc(sizeof(controlt), 1);
|
|
*(u16 *)(c->buf + 4) = htons(ss->sessions[session].remote_session); // Tunnel
|
|
*(u16 *)(c->buf + 6) = htons(protocol);
|
|
c->length += 8;
|
|
|
|
return c;
|
|
}
|
|
|
|
void ppp_free(controlt *c)
|
|
{
|
|
free(c);
|
|
}
|
|
|
|
controlt *ppp_lcp(u16 s, unsigned char type, char identifier)
|
|
{
|
|
controlt *c;
|
|
|
|
if (!identifier) identifier = ss->sessions[s].ppp_identifier++;
|
|
c = ppp_new(s, PPPLCP);
|
|
*(char *)(c->buf + c->length + 0) = type;
|
|
*(char *)(c->buf + c->length + 1) = identifier;
|
|
*(u16 *)(c->buf + c->length + 2) = ntohs(4);
|
|
c->length += 4;
|
|
|
|
return c;
|
|
}
|
|
|
|
controlt *ppp_ipcp(u16 s, unsigned char type, char identifier)
|
|
{
|
|
controlt *c;
|
|
|
|
if (!identifier) identifier = ss->sessions[s].ppp_identifier++;
|
|
c = ppp_new(s, PPPIPCP);
|
|
*(char *)(c->buf + c->length + 0) = type;
|
|
*(char *)(c->buf + c->length + 1) = identifier;
|
|
*(u16 *)(c->buf + c->length + 2) = ntohs(4);
|
|
c->length += 4;
|
|
|
|
return c;
|
|
}
|
|
|
|
controlt *ppp_pap(u16 s, unsigned char type, char identifier, char *username, char *password)
|
|
{
|
|
controlt *c;
|
|
|
|
if (!identifier) identifier = ss->sessions[s].ppp_identifier++;
|
|
c = ppp_new(s, PPPPAP);
|
|
*(char *)(c->buf + c->length + 0) = type;
|
|
*(char *)(c->buf + c->length + 1) = identifier;
|
|
*(u16 *)(c->buf + c->length + 2) = ntohs(4);
|
|
c->length += 4;
|
|
|
|
*(char *)(c->buf + c->length) = strlen(username) + strlen(suffix);
|
|
memcpy((c->buf + c->length + 1), username, strlen(username));
|
|
memcpy((c->buf + c->length + 1 + strlen(username)), suffix, strlen(suffix));
|
|
c->length += strlen(username) + 1 + strlen(suffix);
|
|
|
|
*(char *)(c->buf + c->length) = strlen(password);
|
|
memcpy((c->buf + c->length + 1), password, strlen(password));
|
|
c->length += strlen(password) + 1;
|
|
|
|
return c;
|
|
}
|
|
|
|
void ppp_send(controlt *c)
|
|
{
|
|
*(u16 *)(c->buf + 0) = htons(0x0002); // flags/ver
|
|
*(u16 *)(c->buf + 2) = htons(t); // tunnel
|
|
*(u16 *)(c->buf + 10) = ntohs(c->length - 8);
|
|
if (sendto(udpfd, c->buf, c->length, 0, (struct sockaddr *)&gatewayaddr, sizeof(gatewayaddr)) < 0)
|
|
perror("sendto");
|
|
if (htons(*(u16 *)(c->buf + 6)) != PPPIP)
|
|
{
|
|
printf("PPP Sending ");
|
|
dump_ppp_packet(c->buf + 6, c->length - 6);
|
|
}
|
|
}
|
|
|
|
void ppp_add_16(controlt *c, u16 val)
|
|
{
|
|
*(u16 *) (c->buf + c->length) = htons(val);
|
|
c->length += 2;
|
|
}
|
|
|
|
void ppp_add_32(controlt *c, u32 val)
|
|
{
|
|
*(u32 *) (c->buf + c->length) = htons(val);
|
|
c->length += 4;
|
|
}
|
|
|
|
void ppp_add_s(controlt *c, char *val)
|
|
{
|
|
memcpy(c->buf + c->length, val, strlen(val));
|
|
c->length += strlen(val);
|
|
}
|
|
|
|
void ppp_lcp_add_option(controlt *c, unsigned char option, unsigned char length, int data)
|
|
{
|
|
*(char *)(c->buf + c->length + 0) = option;
|
|
*(char *)(c->buf + c->length + 1) = length + 2;
|
|
memcpy(c->buf + c->length + 2, &data, length);
|
|
c->length += 2 + length;
|
|
}
|
|
|
|
void dump_ppp_packet(char *packet, int l)
|
|
{
|
|
char *p = packet;
|
|
int protocol ;
|
|
if (*(unsigned char *)p == 0xff) p += 2;
|
|
protocol = ntohs(*(u16 *)(p));
|
|
printf("PPP Packet\n");
|
|
switch (protocol)
|
|
{
|
|
case PPPCCP : printf(" Protocol: PPPCCP\n"); break;
|
|
}
|
|
if (protocol == PPPLCP)
|
|
{
|
|
printf(" Protocol: PPPLCP\n");
|
|
printf(" LCP Code: %s\n", lcp_codes[*(u8 *)(p + 2)]);
|
|
}
|
|
else if (protocol == PPPPAP)
|
|
{
|
|
printf(" Protocol: PPPPAP\n");
|
|
if (*(char *)(p + 2) == 2)
|
|
{
|
|
printf(" Authentication accepted\n");
|
|
}
|
|
else if (*(char *)(p + 2) == 3)
|
|
{
|
|
printf(" Authentication denied\n");
|
|
}
|
|
}
|
|
else if (protocol == PPPIPCP)
|
|
{
|
|
printf(" Protocol: PPPIPCP\n");
|
|
printf(" IPCP Code: %s\n", lcp_codes[*(u8 *)(p + 2)]);
|
|
printf(" Address: %s\n", inet_toa(*(u32 *)(p + 8)));
|
|
}
|
|
else if (protocol == PPPIP)
|
|
{
|
|
struct iphdr *iph;
|
|
struct protoent *pr;
|
|
|
|
iph = (struct iphdr *)(p + 2);
|
|
|
|
printf(" Protocol: PPPIP\n");
|
|
printf(" Length: %d\n", l);
|
|
printf(" IP Version: %d\n", iph->version);
|
|
if (iph->version != 4) return;
|
|
pr = getprotobynumber(iph->protocol);
|
|
printf(" IP Header Length: %d\n", iph->ihl);
|
|
printf(" IP TTL: %d\n", iph->ttl);
|
|
printf(" IP Protocol: %s (%d)\n", (pr ? pr->p_name : "unknown"), iph->protocol);
|
|
printf(" IP Checksum: %x\n", ntohs(iph->check));
|
|
}
|
|
else
|
|
{
|
|
printf(" Protocol: unknown 0x%x\n", protocol);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
char *inet_toa(unsigned long addr)
|
|
{
|
|
struct in_addr in;
|
|
memcpy(&in, &addr, sizeof(unsigned long));
|
|
return inet_ntoa(in);
|
|
}
|
|
|
|
// }}}
|
|
|