/* * Simple example program to log packets received with the ulog * watcher of ebtables. * * usage: * Add the appropriate ebtables ulog rule, e.g. (0 < NLGROUP < 33): * ebtables -A FORWARD --ulog-nlgroup NLGROUP * Start this application somewhere: * test_ulog NLGROUP * * compile with make test_ulog KERNEL_INCLUDES= * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../include/ebtables_u.h" #include "../../include/ethernetdb.h" #include /* doesn't hand this to userspace :-( */ #define VLAN_HLEN 4 struct vlan_hdr { unsigned short TCI; unsigned short encap; }; static struct sockaddr_nl sa_local = { .nl_family = AF_NETLINK, .nl_groups = 1, }; static struct sockaddr_nl sa_kernel = { .nl_family = AF_NETLINK, .nl_pid = 0, .nl_groups = 1, }; static const char *hookstr[NF_BR_NUMHOOKS] = { [NF_BR_POST_ROUTING] "POSTROUTING", [NF_BR_PRE_ROUTING] "PREROUTING", [NF_BR_LOCAL_OUT] "OUTPUT", [NF_BR_LOCAL_IN] "INPUT", [NF_BR_BROUTING] "BROUTING", [NF_BR_FORWARD] "FORWARD" }; #define DEBUG_QUEUE 0 #define BUFLEN 65536 static char buf[BUFLEN]; static int sfd; /* Get the next ebt_ulog packet, talk to the kernel if necessary */ ebt_ulog_packet_msg_t *ulog_get_packet() { static struct nlmsghdr *nlh = NULL; static int len, remain_len; static int pkts_per_msg = 0; ebt_ulog_packet_msg_t *msg; socklen_t addrlen = sizeof(sa_kernel); if (!nlh) { recv_new: if (pkts_per_msg && DEBUG_QUEUE) printf("PACKETS IN LAST MSG: %d\n", pkts_per_msg); pkts_per_msg = 0; len = recvfrom(sfd, buf, BUFLEN, 0, (struct sockaddr *)&sa_kernel, &addrlen); if (errno == EINTR) goto recv_new; if (addrlen != sizeof(sa_kernel)) { printf("addrlen %d != %d\n", addrlen, sizeof(sa_kernel)); exit(-1); } if (len == -1) { perror("recvmsg"); exit(-1); } nlh = (struct nlmsghdr *)buf; if (nlh->nlmsg_flags & MSG_TRUNC || len > BUFLEN) { printf("Packet truncated"); exit(-1); } if (!NLMSG_OK(nlh, BUFLEN)) { perror("Netlink message parse error\n"); return NULL; } } msg = NLMSG_DATA(nlh); remain_len = (len - ((char *)nlh - buf)); if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_type != NLMSG_DONE) nlh = NLMSG_NEXT(nlh, remain_len); else nlh = NULL; pkts_per_msg++; return msg; } int main(int argc, char **argv) { int i, curr_len, pktcnt = 0; int rcvbufsize = BUFLEN; ebt_ulog_packet_msg_t *msg; struct ethhdr *ehdr; struct ethertypeent *etype; struct protoent *prototype; struct iphdr *iph; struct icmphdr *icmph; struct tm* ptm; char time_str[40], *ctmp; if (argc == 2) { i = strtoul(argv[1], &ctmp, 10); if (*ctmp != '\0' || i < 1 || i > 32) { printf("Usage: %s \nWith 0 < group " "number < 33\n", argv[0]); exit(0); } sa_local.nl_groups = sa_kernel.nl_groups = 1 << (i - 1); } sa_local.nl_pid = getpid(); sfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_NFLOG); if (!sfd) { perror("socket"); exit(-1); } if (bind(sfd, (struct sockaddr *)(&sa_local), sizeof(sa_local)) == -1) { perror("bind"); exit(-1); } i = setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &rcvbufsize, sizeof(rcvbufsize)); while (1) { if (!(msg = ulog_get_packet())) continue; if (msg->version != EBT_ULOG_VERSION) { printf("WRONG VERSION: %d INSTEAD OF %d\n", msg->version, EBT_ULOG_VERSION); continue; } printf("PACKET NR: %d\n", ++pktcnt); iph = NULL; curr_len = ETH_HLEN; printf("INDEV=%s\nOUTDEV=%s\nPHYSINDEV=%s\nPHYSOUTDEV=%s\n" "PREFIX='%s'", msg->indev, msg->outdev, msg->physindev, msg->physoutdev, msg->prefix); ptm = localtime(&msg->stamp.tv_sec); strftime (time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", ptm); printf("\nARRIVAL TIME: %s\nMARK=%lu\nHOOK=%s\n", time_str, msg->mark, hookstr[msg->hook]); if (msg->data_len < curr_len) { printf("====>Packet smaller than Ethernet header " "length<====\n"); goto letscontinue; } printf("::::::::ETHERNET:HEADER::::::::\n"); ehdr = (struct ethhdr *)msg->data; printf("MAC SRC=%s\n", ether_ntoa((const struct ether_addr *) ehdr->h_source)); printf("MAC DST=%s\nETHERNET PROTOCOL=", ether_ntoa( (const struct ether_addr *)ehdr->h_dest)); etype = getethertypebynumber(ntohs(ehdr->h_proto)); if (!etype) printf("0x%x\n", ntohs(ehdr->h_proto)); else printf("%s\n", etype->e_name); if (ehdr->h_proto == htons(ETH_P_8021Q)) { struct vlan_hdr *vlanh = (struct vlan_hdr *) (((char *)ehdr) + curr_len); printf("::::::::::VLAN:HEADER::::::::::\n"); curr_len += VLAN_HLEN; if (msg->data_len < curr_len) { printf("====>Packet only contains partial " "VLAN header<====\n"); goto letscontinue; } printf("VLAN TCI=%d\n", ntohs(vlanh->TCI)); printf("VLAN ENCAPS PROTOCOL="); etype = getethertypebynumber(ntohs(vlanh->encap)); if (!etype) printf("0x%x\n", ntohs(vlanh->encap)); else printf("%s\n", etype->e_name); if (ehdr->h_proto == htons(ETH_P_IP)) iph = (struct iphdr *)(vlanh + 1); } else if (ehdr->h_proto == htons(ETH_P_IP)) iph = (struct iphdr *)(((char *)ehdr) + curr_len); if (!iph) goto letscontinue; curr_len += sizeof(struct iphdr); if (msg->data_len < curr_len || msg->data_len < (curr_len += iph->ihl * 4 - sizeof(struct iphdr))) { printf("====>Packet only contains partial IP " "header<====\n"); goto letscontinue; } printf(":::::::::::IP:HEADER:::::::::::\n"); printf("IP SRC ADDR="); for (i = 0; i < 4; i++) printf("%d%s", ((unsigned char *)&iph->saddr)[i], (i == 3) ? "" : "."); printf("\nIP DEST ADDR="); for (i = 0; i < 4; i++) printf("%d%s", ((unsigned char *)&iph->daddr)[i], (i == 3) ? "" : "."); printf("\nIP PROTOCOL="); if (!(prototype = getprotobynumber(iph->protocol))) printf("%d\n", iph->protocol); else printf("%s\n", prototype->p_name); if (iph->protocol != IPPROTO_ICMP) goto letscontinue; icmph = (struct icmphdr *)(((char *)ehdr) + curr_len); curr_len += 4; if (msg->data_len < curr_len) { truncated_icmp: printf("====>Packet only contains partial ICMP " "header<====\n"); goto letscontinue; } if (icmph->type != ICMP_ECHO && icmph->type != ICMP_ECHOREPLY) goto letscontinue; curr_len += 4; if (msg->data_len < curr_len) goto truncated_icmp; /* Normally the process id, it's sent out in machine * byte order */ printf("ICMP_ECHO IDENTIFIER=%u\n", icmph->un.echo.id); printf("ICMP_ECHO SEQ NR=%u\n", ntohs(icmph->un.echo.sequence)); letscontinue: printf("===>Total Packet length: %d, of which we examined " "%d bytes\n", msg->data_len, curr_len); printf("###############################\n" "######END#OF##PACKET#DUMP######\n" "###############################\n"); } return 0; }