一个基本的portknocking demo.
server端运行receive程序,初始化iptable,规则清空;
client端允许send程序,发送端口试探序列,开启相应的tcp端口。
send.c
/* portknocking demo V0.1 moxuansheng */
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#define __FAVOR_BSD
#include <netinet/tcp.h>
#include <stdio.h>
#include <unistd.h>

/* Global variable */

struct portlist {
int port;
struct portlist* next;
};

struct portlist* start = NULL; /* the port list */

struct sockaddr_in server; /* destination socket struct */

int portnum = 0; /* the sum of port knock packet send to dest*/

/* function protype */
void
get_command(char** argv, struct portlist** start, char** dest, char** srcport); /* parser program arg */

char*
is_another_param(char** argv); /* has next arg */

char*
next_param(char*** argv); /* next to arg */

void
addportList(struct portlist** start, char* port); /* add the port list in List */

unsigned short
csum(unsigned short* buf, int nwords); /* packet check sum */

void
sendpacket(int sockfd, char* data, int destport, char* dest, int srcport); /* send the packet to dest */

void
usage(void); /* program usage */


int
main(int argc, char *argv[]) {
int sockfd;
char* dest; /* dest host IP address string */
char* sourceport;
char datagram[4096];
int count; /* send packet count */

/* parser the argument */
get_command(argv, &start, &dest, &sourceport);

#ifdef DEBUG2
struct portlist* temp = start;
printf("the port sequence is: ");
while(temp != NULL) {
printf(" %d ", temp->port);
temp = temp->next;
}
printf(" the port number is:%d ",portnum);
#endif

/* initilize the sockaddr_in */
bzero(&server,sizeof(server));
bzero(datagram,4096);
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(dest);
server.sin_port = htons(0);

/*create raw socket */
if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) == -1) {
printf(" create socket error ");
exit(0);
}

/* send packet with port number in portlist */
struct portlist* current;
for (count = 0; count < portnum; count++) {
current = start;
sendpacket(sockfd, datagram,current->port, dest, atoi(sourceport));
start = start->next;
free(current);
}
return 0;
}

void
get_command(char** argv, struct portlist** start, char** dest, char** srcport) {
char c;
char* conf;
char* port;
if (!*(argv+1)){
printf("missing argument ");
usage();
exit(0);
}
/* parser the program argument */
while(*++argv) {
if ((*argv)[0] != '-') {
printf(" missing switch character at '%s'.Use -h for option.",*argv);
exit(0);
}
if (!(c = (*argv)[1])) {
printf(" missing switch character at '%s'.Use -h for option.",*argv);
exit(0);
}
if ((*argv)[2]) {
printf(" joined argument is not allowed ");
exit(0);
}
switch(c) {
case 'h':
usage();
break;
case 'd':
{
if ( !is_another_param(argv)){
printf("ERR: -d need one argument ");
usage();
exit(0);
}
*dest = next_param(&argv);
if (is_another_param(argv)) {
printf("ERR:no more two destination hosts ");
usage();
exit(0);
}
}
break;
case 'p':
if ( !is_another_param(argv)){
printf("ERR: -p need one argument ");
usage();
exit(0);
}
while((port=next_param(&argv))) {
addportList(start,port);
++portnum;
}
break;
case 's':
if (!is_another_param(argv)) {
printf("ERR: -s need one argument ");
usage();
exit(0);
}
*srcport = next_param(&argv);
if (is_another_param(argv)) {
printf("ERR: no more two source port ");
usage();
exit(0);
}
break;
default:
{
usage();
exit(0);
}
}
}


}

char*
is_another_param(char** argv) {
return (*(argv + 1) && *(argv + 1)[0] != '-')
? *(argv + 1)
: NULL;
}
char*
next_param(char*** argv) {
char* c;
if ((c = is_another_param(*argv))) {
(*argv)++;
return c;
}
return NULL;
}

/* add the port knock sequence in the portlist*/
void
addportList(struct portlist** start, char* port) {
struct portlist* newport, *currentport, * previousport;
currentport = *start;
previousport = NULL;
newport = (struct portlist*)malloc(sizeof(struct portlist));
if (newport == NULL) {
printf("malloc portlist struct failed ");
exit(0);
}
if ((newport->port = atoi(port)) > 65535) {
printf(" the port out of range ");
exit(0);
}
newport->next = NULL;
while (currentport != NULL) {
previousport = currentport;
currentport = currentport->next;
}
if (*start == NULL) {
*start = newport;
} else {
previousport->next = newport;
}

}

/* check sum, copy from the raw socket programming */
unsigned short
csum(unsigned short* buf, int nwords) {
unsigned long sum;
for (sum = 0; nwords > 0; nwords--)
sum += *buf++;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ~sum;
}

/*send the packet to destination using raw socket */
void
sendpacket(int sockfd, char* datagram, int destport, char* dest, int srcport) {
struct ip *iph = (struct ip*)datagram;
struct tcphdr *tcph = (struct tcphdr*)(datagram + sizeof(struct ip));
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = 0;
iph->ip_len = sizeof(struct ip) + sizeof(struct tcphdr);
iph->ip_id = htonl(12345);
iph->ip_off = 0;
iph->ip_ttl = 255;
iph->ip_p = 6;
iph->ip_sum = 0;
iph->ip_src.s_addr = inet_addr("127.0.0.1");
iph->ip_dst.s_addr = inet_addr(dest);
tcph->th_sport = htons(srcport);
tcph->th_dport = htons(destport);
tcph->th_seq = random();
tcph->th_ack = 0;
tcph->th_x2 = 0;
tcph->th_off = 0;
tcph->th_flags = TH_SYN;
tcph->th_win = htonl(65535);
tcph->th_sum = 0;
tcph->th_urp = 0;
iph->ip_sum = csum ((unsigned short*)datagram, iph->ip_len >> 1);
int one = 1;
const int *val = &one;
if (setsockopt(sockfd,IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0) {
printf("Cannot set HDRINCL ");
exit(0);
}
if (sendto(sockfd, datagram, iph->ip_len, 0, (struct sock_addr*)&server, sizeof(server)) < 0){
printf("send error ");
exit(0);
} else {
printf("send ok ");
}


}

/* usage */
void
usage(void) {
printf("-d the destination server ");
printf("-p the port sequence sending to dest ");
printf("-s the sourceport of packet sending to dest ");
printf(" the usage is: ");
printf(" -d dest -p port1 port2 -s port3 ");
printf(" the example is: ");
printf(" arg -d 192.168.100.200 -p 1024 1025 1026 -s 1981 ");
}
receive.c
/* portknocking demo V0.1 moxuansheng*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pcap.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#define __FAVOR_BSD
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <syslog.h>
#define PCAP_FILTER_1 "tcp src port " /* pcap filter */
#define PCAP_FILTER_2 " or " /* pcap filter */
#define CLOSE_PORT "9999" /* close the firewall */
#define IPTABLE_PATH "/sbin/iptables"

#define SUCCESS 0
#define ERROR 1

#define FOUND 0
#define NOTFOUND 1

/* Global variable */
enum{
CLOSE,
OPEN
};

struct portlist {
int port;
struct portlist* next;
}; /* the list store port sequence */

struct hostnode {
int stat; /* the host stat opening or close */
int steps; /* the port sequence steps */
char* addr;/* host addr */
struct hostnode* next;
};

struct portlist* portstart = NULL; /* port list start node */

int portnum = 0; /* the number of port sequence */

int *portarray; /* the the port array, access directly without list */

struct hostnode* hoststart = NULL; /* the host start node */

pcap_t *session; /* pcap session handler */

char* cli_addr; /* the client ip address */

/* function prototype */
void
get_command(char** argv, char** interface, char** sourceport, struct portlist** start); /* parser program arg */

char*
is_another_param(char** argv); /* fetch next arg */

char*
next_param(char*** argv); /* get arg and step to next arg */

void
iptables_init(void); /* iptables init */

void
addportList(struct portlist** portstart, char* port); /* add the port to List */

int
addhostList(struct hostnode** hoststart, char* cli_addr, int destport); /* add the receive ip into a host list*/

int
checkhostList(struct hostnode** start, char* cli_addr, int destport); /* check the destport if the cli_addr insert into hostnode List or not */

int
checkportList(struct portlist** start, int destport); /* check whether the destport of received packet is in portlist or not */

int
pcap_init(char* , char*); /* init the libpcap */

void
print_error(pcap_t* session); /* print the pcap session err*/

void
receive_pkt(u_char* args, const struct pcap_pkthdr* header, const u_char* packet); /* process a receive packet */

void*
emalloc(size_t size, int num); /* malloc size of memory */

void
usage(void); /* the program usage */



int
main(int argc, char* argv[]) {

char* interface; /* network interface */

char* sourceport; /* the packet's sourceport */

get_command(argv, &interface, &sourceport, &portstart);

///////////////////
//run in daemon, you can add standard method
///////////////////
#ifdef DEBUG2
printf(" the interface is:%s ", interface);
struct portlist* temp = portstart;
printf("the port sequence is: ");
while (temp) {
printf(" %d ",temp->port);
temp = temp->next;
}
#endif

/* copy the portlist to array, so it could access directly without all list*/
portarray = (int*) emalloc(sizeof(int), portnum);
int i = 0;
struct portlist* pstart = portstart;
while (pstart != NULL) {
portarray[i++] = pstart->port;
pstart = pstart->next;
}
#ifdef DEBUG2
printf("the port array is: ");
for (i = 0; i < portnum; i++)
printf(" %d ",portarray[i]);
#endif

/* iptable init */
iptables_init();

/* pcap init */
if(pcap_init(interface, sourceport)) {
printf("pcap init Error ");
return ERROR;
}
/* this is main loop program, capature the matched packet */
pcap_loop(session, -1, &receive_pkt, NULL);
return SUCCESS;
}

void
get_command(char** argv, char** interface, char** sourceport, struct portlist** portstart) {
char c;
char* port;
if (!*(argv + 1)) {
printf("ERR: missing argument ");
usage();
exit(0);
}
while(*++argv) {
if ((*argv)[0] != '-') {
printf(" missing switch character at '%s'.Use -h for option.",*argv);
usage();
exit(0);
}
if (!(c = (*argv)[1])) {
printf(" missing argument at '%s'.Use -h for option.",*argv);
usage();
exit(0);
}
if ((*argv)[2]) {
printf(" joined argument is not allowed ");
usage();
exit(0);
}
switch(c) {
case 'h':
usage();
exit(0);
break;
/* get the interface */
case 'i':
{
if (!is_another_param(argv)) {
printf(" -i option need one arg ");
usage();
exit(0);
}
*interface = next_param(&argv);
if (is_another_param(argv)) {
printf("ERR:no more two interface ");
usage();
exit(0);
}
break;
}
/* get the sourceport and compare it with the received packet's srouceport*/
case 's':
{
if (!is_another_param(argv)) {
printf(" -s option need one arg ");
usage();
exit(0);
}
break;
}
/* get the port sequece and store them in port list */
case 'p':
{
if (!is_another_param(argv)) {
printf(" -p option need at least one arg ");
usage();
exit(0);
}
while((port = next_param(&argv))) {
addportList(portstart,port);
portnum++;
}
break;
}
default:
usage();
exit(0);
}
}
}

char*
is_another_param(char** argv) {
return (*(argv + 1) && *(argv+1)[0] != '-')
? *(argv + 1)
: NULL;
}

char*
next_param(char*** argv) {
char* c;
if ((c = is_another_param(*argv))) {
(*argv)++;
return c;
}
return NULL;
}


void
addportList(struct portlist** portstart, char* port) {

struct portlist* newport, *currentport, *previousport;

currentport = *portstart;

newport = (struct portlist*)malloc(sizeof(struct portlist));

previousport = NULL;

if (newport == NULL) {
printf("malloc struct portlist failed ");
exit(0);
}
if((newport->port = atoi(port)) > 65535) {
printf("port out of range ");
exit(0);
}

newport->next = NULL;
while ( currentport != NULL) {
previousport = currentport;
currentport = currentport->next;
}
if ((*portstart)== NULL) {
*portstart = newport;
} else {
previousport->next = newport;
}
}

void*
emalloc(size_t size, int num) {
void *p;
p = malloc(size * num);
if (p == NULL) {
printf("malloc error ");
exit(0);
}else {
printf("malloc success ");
}
return p;
}

int
pcap_init(char* interface, char* sourceport) {

#ifdef DEBUG2
printf("pcap-initting ");
#endif

char* dev = interface;
char errbuf[PCAP_ERRBUF_SIZE]; /* Error string returned, see pcap manpage */
struct bpf_program filter; /* The compiled filter */
char filter_string1[] = PCAP_FILTER_1;
char filter_string2[] = PCAP_FILTER_2;
bpf_u_int32 mask; /* Our netmask */
bpf_u_int32 net; /* Our IP */

strncat(filter_string1, sourceport, strlen(sourceport));
strncat(filter_string1, filter_string2, strlen(filter_string2));
strncat(filter_string1, CLOSE_PORT, strlen(CLOSE_PORT));
if ((pcap_lookupnet(dev, &net, &mask, errbuf)) < 0 )
{
printf("Error looking up pcap device: %s ", errbuf);
return ERROR;
}

/* open the pcap session */
if ( (session = pcap_open_live(dev, BUFSIZ, 0, 0, errbuf)) == NULL)
{
printf("Error with pcap_open_live: %s ", errbuf);
return ERROR;
}

/* Compile a new filter */
if ((pcap_compile(session, &filter, filter_string1, 0, net)) < 0)
{
print_error(session);
return ERROR;
}

if ( (pcap_setfilter(session, &filter)) < 0)
{
print_error(session);
return ERROR;
}
return SUCCESS;
}

void
print_error(pcap_t *s)
{
/* From the pcap manpage:
void pcap_perror(pcap_t *p, char *prefix) */
char *prefix = "The following pcap error occurred";
pcap_perror(s, prefix);
}

void
receive_pkt(u_char* args, const struct pcap_pkthdr* header, const u_char* packet) {

#ifdef DEBUG2
printf("receive a packet ");
#endif

int found = 0;
pid_t pid;
const struct ether_header *ethernet_header;
const struct ip *iphdr;
const struct tcphdr *tcpheader;
int size_ether = sizeof(struct ether_header);
int size_ip = sizeof(struct ip);
int destport;
int srcport;
ethernet_header = (struct ether_header*)packet;
iphdr = (struct ip*)(packet + size_ether);
tcpheader = (struct tcphdr*)(packet + size_ether + size_ip);
cli_addr = inet_ntoa(iphdr->ip_src); /* get the src */
destport = ntohs(tcpheader->th_dport); /* get the src port */
srcport = ntohs(tcpheader->th_sport);
/* the the received packet's sourceport is the close port number, remove the cli from host list, and delete iptable rules */
if (srcport == atoi(CLOSE_PORT)) {
struct hostnode *previous, *current;
previous = NULL;
if (hoststart == NULL) {
printf(" the host List is null ");
return;
}

for (current = hoststart; current != NULL; previous = current, current = current->next) {
if (!strcmp(current->addr, cli_addr)) {
found = 1;
/* if the host is in the first host list*/
if(previous == NULL) {
hoststart = current->next;
} else {
previous->next = current->next;
}
free(current);
break;
}
}
if (found) {
syslog(LOG_ERR, "delete the %s IP in the host list. ", cli_addr);
if ((pid = fork()) == 0) {
execlp(IPTABLE_PATH, "iptables", "-D", "INPUT", "-s", cli_addr, "-p", "tcp", "-j", "ACCEPT", NULL);
exit(0);
}
printf("delete the IP %s from the trusted list. ", cli_addr);
} else {
printf(" the cli_addr not found in the host list ");
}
return ;
}

#ifdef DEBUG2
printf(" received packet's dest port is:%d ",ntohs(tcpheader->th_dport));
#endif

if (!checkhostList(&hoststart, cli_addr, destport)) {
printf("add the cli_addr in the hostnode List ");
} else {
printf(" the received packet not comply rule ");
}
}


/* (1) check wherther the desport in the portList, if not, the packet received is not a valid packet;
(2) if it's a valid packet, add the cli_addr in hostnode List and modify the status
*/
int
checkhostList(struct hostnode** start, char* cli_addr, int destport) {

if (!checkportList(&portstart,destport)) {
printf(" the destport in the trusted port list ");
if(!addhostList(start, cli_addr, destport)) {
printf("add the host in the list ");
}
else {
return ERROR;
}
} else {
printf(" it's not a valid packet, drop it ");
return ERROR;
}
return SUCCESS;
}

/* check if the received packet's destport in the portlist */
int
checkportList(struct portlist** portstart, int destport) {
struct portlist * currentport;
currentport = *portstart;
while (currentport != NULL) {
if (currentport->port == destport) {
return FOUND;
}
currentport = currentport->next;
}
return NOTFOUND;
}

int
addhostList(struct hostnode** start, char* cli_addr, int destport) {
pid_t pid;
struct hostnode *newhost, *currenthost, *previoushost;
int count; /* compare the destport to portarray */
newhost = (struct hostnode*)malloc(sizeof(struct hostnode));
if (newhost == NULL) {
printf("malloc hostnode failed ");
exit(0);
}
newhost->stat = CLOSE;
newhost->steps = 0;
newhost->addr = cli_addr;
newhost->next = NULL;
currenthost = *start;
previoushost = NULL;

while (currenthost != NULL) {
printf(" in the host list ");
/* find the host whether already add in the host list*/
if (!strcmp(currenthost->addr, newhost->addr)) {
/* if the host's stat is already OPEN, skip the knock sequence */
if (currenthost->stat == OPEN) {
printf(" the host's is already open ");
free(newhost);
return SUCCESS;
}
/* check if the port sequence is right*/
printf(" the currenthost's steps is:%d ",currenthost->steps);
if (portarray[(currenthost->steps + 1)] == destport) {
printf(" follow the sequence ");
++currenthost->steps;
printf(" the steps is:%d ",currenthost->steps);

/* if the steps is the last key in portarray, add the trusted
host list*/
if (currenthost->steps == (portnum - 1)) {
printf("the port sequece is right,add open the firewall ");
currenthost->stat = OPEN;

/* add the cli_addr in the trusted host list */
syslog(LOG_ERR, "open tcp port for IP:%s. ",cli_addr);
if ((pid = fork()) == 0) {
execlp(IPTABLE_PATH, "iptables", "-I", "INPUT", "1", "-s", cli_addr, "-p", "tcp", "-j", "ACCEPT", NULL);
exit(0);
}


return SUCCESS;
}
return SUCCESS;
}
/* if ++steps portarray key is not equal the destport, the port se
quence is wrong */
else {
printf("sequence is wrong ");
return ERROR;
}
previoushost = currenthost;
currenthost = currenthost->next;

}
}
/* if the host is the not in the trust list, check the first port whether right or not */
if (previoushost == NULL) {
if (destport == portarray[0]) {
*start = newhost;
printf("the start host node ");
}
else {
printf("the first port number is wrong ");
return ERROR;
}
}
/* if the has the start node, insert the new host in the tail */
else {
if (destport == portarray[0]) {
previoushost = newhost;
}
else {
printf("the first port number is wrong in the host list ");
return ERROR;
}
}
return SUCCESS;
}

/* get from the cryptoknocking, maybe that the open source :-)*/
void
iptables_init(){
pid_t pid;
syslog(LOG_ERR,"portknocking starting.");
/* Flush the default rules */
if((pid=fork())==0){
execlp(IPTABLE_PATH,"iptables","-F",NULL);
exit(0);
}
/* Add the state modules */
if((pid=fork())==0){
execlp(IPTABLE_PATH,"iptables","-A","INPUT","-m","state","--state",
"ESTABLISHED,RELATED","-j","ACCEPT",NULL);
exit(0);
}

if((pid=fork())==0){
execlp(IPTABLE_PATH,"iptables","-A","OUTPUT","-m","state","--state",
"NEW,ESTABLISHED,RELATED","-j","ACCEPT",NULL);
exit(0);
}

/* Default policy is DROP */
if((pid=fork())==0){
execlp(IPTABLE_PATH,"iptables","-A","INPUT","-p","tcp","-j","DROP",
NULL);
exit(0);
}
syslog(LOG_ERR, "portknocking monitor all incoming traffic ");
}
void
usage(void) {
printf("the portknocking deamon ");
printf("-i the interface listened ");
printf("-p the port knocking sequence ");
printf("-s the source port of packet received ");
printf(" usage: -i eth0 -s 1981 -p 1025 1026 1027 ");
}
server端运行receive程序,初始化iptable,规则清空;
client端允许send程序,发送端口试探序列,开启相应的tcp端口。
send.c






















































































































































































































































































receive.c




























































































































































































































































































































































































































































































































































































