Compile with: gcc -std=gnu11 -o "portknock" "portknock.c" -lpcap
I did this as a learning experience, and it actually ended up being something useful. I made a port-knocking spawned reverse TTY with it, and it could easily be made into a port knocking firewall daemon.
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <netinet/if_ether.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <time.h>
#include <string.h>
#include <linux/kd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
struct knock {
struct sockaddr* addr;
int prog;
};
struct knock** np = NULL;
size_t nps = 0;
uint16_t kxs[] = { 5900, 80, 88, 82, 86 };
size_t kxsn = 5;
int main() {
if (getuid() != 0) {
printf("Port Knocking requires root!\n");
}
char errbuf[PCAP_ERRBUF_SIZE];
char *dev = pcap_lookupdev(errbuf);
if (dev == NULL) {
printf("pcap_lookupdev %s\n", errbuf);
exit(1);
}
pcap_t* descr = pcap_open_live(dev, 8192, 0, 100, errbuf);
if (descr == NULL) {
printf("pcap_open_live %s\n", errbuf);
exit(1);
}
struct pcap_pkthdr hdr;
struct pcap_pkthdr* hdrp = &hdr;
const u_char* packet;
int ret;
while (1) {
ret = pcap_next_ex(descr, &hdrp, &packet);
if (ret < 0) {
printf("pcap receive error, %s\n", pcap_geterr(descr));
exit(1);
} else if (ret == 0) {
//printf("timeout!\n");
continue;
}
const struct ether_header *eptr = (struct ether_header *) packet;
uint16_t et = ntohs(eptr->ether_type);
u_int size_ip;
struct sockaddr* from;
struct sockaddr_in ip4;
struct sockaddr_in6 ip6;
if (et == ETHERTYPE_IP) {
const struct iphdr *ip = (struct iphdr*) (packet + ETHER_HDR_LEN);
size_ip = ip->ihl * 4;
if (size_ip < 20) {
printf("bad ip header size: %i\n", size_ip);
continue;
}
if (ip->protocol != 6) continue;
from = (struct sockaddr*) &ip4;
ip4.sin_family = AF_INET;
ip4.sin_addr.s_addr = ip->saddr;
} else if (et == ETHERTYPE_IPV6) {
const struct ip6_hdr *ip = (struct ip6_hdr*) (packet + ETHER_HDR_LEN);
size_ip = 40;
uint8_t et = ip->ip6_ctlun.ip6_un1.ip6_un1_nxt;
if (et == 0 || et == 60 || et == 43 || et == 44 || et == 51 || et == 50 || et == 60 || et == 135) {
struct ip6_ext *ext = (struct ip6_ext*) (packet + ETHER_HDR_LEN + size_ip);
size_ip += 8 + (ext->ip6e_len * 8);
et = ext->ip6e_nxt;
while (et == 0 || et == 60 || et == 43 || et == 44 || et == 51 || et == 50 || et == 60 || et == 135) {
size_ip += 8 + (ext->ip6e_len * 8);
ext = (struct ip6_ext*) (packet + ETHER_HDR_LEN + size_ip);
}
if (et == 59) continue;
} else if (et == 59) continue;
if (et != 6) continue;
from = (struct sockaddr*) &ip6;
ip6.sin6_family = AF_INET6;
ip6.sin6_addr = ip->ip6_src;
//printf("ipv6 size: %u\n", size_ip);
} else continue;
const struct tcphdr *tcp = (struct tcphdr*) (packet + ETHER_HDR_LEN + size_ip);
u_int size_tcp = tcp->th_off * 4;
if (size_tcp < 20) {
printf("bad tcp header size: %i\n", size_tcp);
continue;
}
//const u_char* data = (u_char *) (packet + ETHER_HDR_LEN + size_ip + size_tcp);
if ((tcp->th_flags & TH_SYN) == TH_SYN) {
//char* add;
//char ip6a[64];
//if (from->sa_family == AF_INET) {
// add = inet_ntoa(ip4.sin_addr);
//} else if (from->sa_family == AF_INET6) {
// add = inet_ntop(AF_INET6, &ip6.sin6_addr, ip6a, 64);
//}
uint16_t dport = ntohs(tcp->th_dport);
//uint16_t sport = ntohs(tcp->th_sport);
//printf("%s sent a syn to port dest = %u, source = %u\n", add, dport, sport);
struct knock* cn = NULL;
for (int i = 0; i < nps; i++) {
if (np[i]->addr->sa_family == AF_INET && from->sa_family == AF_INET) {
if (((struct sockaddr_in*) (np[i]->addr))->sin_addr.s_addr == ip4.sin_addr.s_addr) {
cn = np[i];
}
}
}
int cp = cn == NULL ? 0 : cn->prog;
if (dport == kxs[cp]) {
if (++cp == kxsn) {
printf("knock complete!\n");
//do knock stuff, ie accept a connection from an IP, by interfacing with libiptc
} else {
printf("knock progress = %i!\n", cp);
if (cn == NULL) {
cn = malloc(sizeof(struct knock));
if (cn == NULL) {
printf("out of memory!\n");
exit(1);
}
cn->prog = 1;
size_t s = from->sa_family == AF_INET ? sizeof(ip4) : sizeof(ip6);
cn->addr = malloc(s);
if (cn->addr == NULL) {
printf("out of memory!\n");
exit(1);
}
memcpy(cn->addr, from, s);
if (np == NULL) {
np = malloc(sizeof(struct knock*));
nps = 0;
} else {
np = realloc(np, sizeof(struct knock*) * (nps + 1));
}
if (np == NULL) {
printf("out of memory!\n");
exit(1);
}
np[nps++] = cn;
} else {
cn->prog++;
}
}
}
}
}
exit(0);
return EXIT_SUCCESS;
}
et == 60
twice on theet == 0 || et == 60 || et == 43 || et == 44 || et == 51 || et == 50 || et == 60 || et == 135
lines? \$\endgroup\$