/* Copyright (C) 2000 - 2006 Christian Kreibich . Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies of the Software and its documentation and acknowledgment shall be given in the documentation and software packages that this Software was used. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifdef HAVE_CONFIG_H # include #endif #include #ifdef LINUX_HOST struct ipovly { u_char ih_x1[9]; u_char ih_pr; u_int16_t ih_len; struct in_addr ih_src; struct in_addr ih_dst; }; #endif static LND_Protocol *udp; static LND_Protocol * udp_get_ip(void) { static LND_Protocol *ip = NULL; if (!ip) ip = libnd_proto_registry_find(LND_PROTO_LAYER_NET, 0x0800); return ip; } static gboolean udp_header_complete(guchar *data, guchar *data_end) { if (!data) return FALSE; /* D(("UDP header complete? %p %p --> %i\n", data, data_end, (data + 8 <= data_end))); */ return (data + 8 <= data_end); } static gboolean udp_get_first(const LND_Packet *packet, struct ip **iphdr, struct udphdr **udphdr) { GList *l; LND_Protocol *ip; LND_ProtoData *pd, *pd_prev; if (!packet || !iphdr || !udphdr) return FALSE; if (! (ip = udp_get_ip())) return FALSE; /* Walk the proto data and find the first UDP (we don't support nesting UDP) with a preceding IP */ for (l = packet->pd; l; l = g_list_next(l)) { pd = (LND_ProtoData *) l->data; if (g_list_previous(l) && pd->inst.proto == udp) { pd_prev = (LND_ProtoData *) g_list_previous(l)->data; if (pd_prev->inst.proto == ip) { *iphdr = (struct ip *) pd_prev->data; *udphdr = (struct udphdr *) pd->data; return TRUE; } } } return FALSE; } /* Plugin hook implementations: ---------------------------------------- */ const char * name(void) { return ("UDP Plugin"); } const char * description(void) { return ("A plugin providing support for the UDP protocol.\n"); } const char * author(void) { return ("Christian Kreibich, "); } const char * version(void) { return VERSION; } LND_Protocol * init(void) { udp = libnd_proto_new("UDP", LND_PROTO_LAYER_TRANS, IPPROTO_UDP); udp->init_packet = libnd_udp_init_packet; udp->header_complete = libnd_udp_header_complete; udp->fix_packet = libnd_udp_fix_packet; return udp; } /* Protocol method implementations: ------------------------------------ */ void libnd_udp_init_packet(LND_Packet *packet, guchar *data, guchar *data_end) { struct udphdr *udphdr; LND_Protocol *payload_proto; D_ENTER; udphdr = (struct udphdr *) data; if (!udp_header_complete(data, data_end)) { libnd_raw_proto_get()->init_packet(packet, data, data_end); D_RETURN; } libnd_packet_add_proto_data(packet, udp, data, data_end); /* If UDP contains any payload, check the appriopriate header field value and demultiplex packet initialization up to the next correct protocol: */ if (! (payload_proto = libnd_proto_registry_find(LND_PROTO_LAYER_APP, ntohs(udphdr->uh_dport)))) payload_proto = libnd_raw_proto_get(); payload_proto->init_packet(packet, data + 8, data_end); D_RETURN; } gboolean libnd_udp_header_complete(const LND_Packet *packet, guint nesting) { LND_ProtoData *pd; if (!packet) return FALSE; if (! (pd = libnd_packet_get_proto_data(packet, udp, nesting))) return FALSE; return udp_header_complete(pd->data, pd->data_end); } gboolean libnd_udp_datagram_complete(const LND_Packet *packet, guint nesting) { LND_ProtoData *pd; struct udphdr *udphdr; if (!packet) return FALSE; if (! (pd = libnd_packet_get_proto_data(packet, udp, nesting))) return FALSE; udphdr = (struct udphdr *) pd->data; return (pd->data + ntohs(udphdr->uh_ulen) <= libnd_packet_get_end(packet)); } gboolean libnd_udp_fix_packet(LND_Packet *packet) { struct udphdr *udphdr; guint16 correct_sum; if (!packet) return FALSE; if (!libnd_udp_csum_correct(packet, &correct_sum)) { udphdr = (struct udphdr*) libnd_packet_get_data(packet, udp, 0); udphdr->uh_sum = correct_sum; libnd_packet_modified(packet); return TRUE; } return FALSE; } guint16 libnd_udp_checksum(const LND_Packet *packet) { LND_Protocol *ip; struct ip* iphdr = NULL; struct udphdr *udphdr = NULL; struct ipovly ipovly; guint16 *w; guint i, preadd; guint16 old, result = 0; if (!packet || !(ip = udp_get_ip())) return 0; if (!udp_get_first(packet, &iphdr, &udphdr)) return 0; w = (guint16 *) &ipovly; preadd = 0; memset(&ipovly, 0, sizeof(struct ipovly)); ipovly.ih_pr = iphdr->ip_p; ipovly.ih_len = udphdr->uh_ulen; ipovly.ih_src = iphdr->ip_src; ipovly.ih_dst = iphdr->ip_dst; for (i = 0; i < sizeof(struct ipovly)/sizeof(guint16) ; i++) preadd += *w++; old = udphdr->uh_sum; udphdr->uh_sum = 0; result = libnd_misc_in_cksum((u_short*)udphdr, ntohs(udphdr->uh_ulen), preadd); udphdr->uh_sum = old; return result; } LND_Protocol * libnd_udp_get(void) { return udp; } gboolean libnd_udp_get_headers(const LND_Packet *packet, struct ip **iphdr, struct udphdr **udphdr) { GList *l; LND_Protocol *ip; LND_ProtoData *pd, *pd_prev; if (!packet || !iphdr || !udphdr) return FALSE; if (! (ip = udp_get_ip())) return FALSE; /* Walk the proto data and find the first UDP * (we don't support nesting UDP) with a preceding IP */ for (l = packet->pd; l; l = g_list_next(l)) { pd = (LND_ProtoData *) l->data; if (g_list_previous(l) && pd->inst.proto == udp) { pd_prev = (LND_ProtoData *) g_list_previous(l)->data; if (pd_prev->inst.proto == ip) { *iphdr = (struct ip *) pd_prev->data; *udphdr = (struct udphdr *) pd->data; return TRUE; } } } return FALSE; } guint libnd_udp_get_payload_length(struct ip *iphdr, struct udphdr *udphdr) { if (!udphdr) return 0; return ntohs(udphdr->uh_ulen) - 8; } gboolean libnd_udp_csum_correct(const LND_Packet *packet, guint16 *correct_sum) { struct udphdr *udphdr; guint16 old_sum, correct_sum_tmp; if (!packet) return FALSE; udphdr = (struct udphdr *) libnd_packet_get_data(packet, libnd_udp_get(), 0); old_sum = udphdr->uh_sum; correct_sum_tmp = libnd_udp_checksum(packet); if (correct_sum) *correct_sum = correct_sum_tmp; return (old_sum == correct_sum_tmp); }