From 48f234331001491a6e8e7b34c076678c6f7e6600 Mon Sep 17 00:00:00 2001 From: Keith Date: Tue, 20 Oct 2020 08:40:50 +0200 Subject: Implement ICMP response for inactive IP address. Send ICMP Host Unreachable packets back on the tun device in reponse to a packet received for an IP address that is not active in our pool (No active pdp context) Only IPv4 implemented. Change-Id: Ia2c708feab14bb4cada00b0a90e0cb56d680d1aa --- ggsn/ggsn.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 267637d..f2c9656 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -580,6 +581,72 @@ err_wrong_af: return 0; } +static uint16_t inet_checksum(void *data, int len) { + int nleft = len; + int sum = 0; + unsigned short *w = data; + unsigned short answer = 0; + + while (nleft > 1) + { + sum += *w++; + nleft -= 2; + } + + if (nleft == 1) + { + *(unsigned char *)(&answer) = *(unsigned char *)w; + sum += answer; + } + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >> 16); + answer = ~sum; + + return (answer); +} + + +/* Generate and send an ICMP HOST UNREACHABLE Packet */ +static void ipv4_host_unreach(struct tun_t *tun, void *pack, unsigned len) { + + char send_buf[sizeof(struct ip) + sizeof(struct icmp) + len]; + /* What are these 20 bytes on the end of pack? */ + len = len - 20; + struct iphdr *iph = (struct iphdr *)pack; + + memset(send_buf, 0, sizeof(send_buf)); + + struct ip *ip = (struct ip *)send_buf; + struct icmp *icmp = (struct icmp *)(ip + 1); + + ip->ip_v = 4; + ip->ip_hl = 5; + ip->ip_tos = 0; + ip->ip_len = htons(sizeof(send_buf)); + ip->ip_id = rand(); + ip->ip_off = 0; + ip->ip_ttl = 64; + ip->ip_sum = 0; + ip->ip_p = IPPROTO_ICMP; + ip->ip_src.s_addr = iph->daddr; + //inet_pton(AF_INET, "10.20.0.4", &(ip->ip_src)); + ip->ip_dst.s_addr = iph->saddr; + + ip->ip_sum = inet_checksum(ip, sizeof(send_buf)); + + icmp->icmp_type = ICMP_DEST_UNREACH; + icmp->icmp_code = ICMP_HOST_UNREACH; + icmp->icmp_id = 0; + icmp->icmp_seq = 0; + icmp->icmp_cksum = 0; + + memcpy(send_buf + sizeof(ip) + sizeof(icmp) + 12, pack, len); + icmp->icmp_cksum = inet_checksum(icmp, sizeof(icmp) + 12 + len); + tun_encaps(tun, send_buf, sizeof(send_buf)); + +} + /* Internet-originated IP packet, needs to be sent via GTP towards MS */ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) { @@ -622,18 +689,30 @@ static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len) return 0; if (ippool_getip(pool, &ipm, &dst)) { - LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s) with no PDP contex! (%s)\n", - apn->cfg.name, + LOGTUN(LOGL_DEBUG, tun, "Received packet from (%s) for APN(%s) but dest is not in pool!\n", iph->version == 4 ? inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)) : - inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr))); + inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)), + apn->cfg.name); return 0; } - LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s)\n", apn->cfg.name); + LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s), EUA(%s)\n", apn->cfg.name, + iph->version == 4 ? + inet_ntop(AF_INET, &iph->daddr, straddr, sizeof(straddr)) : + inet_ntop(AF_INET6, &ip6h->ip6_dst, straddr, sizeof(straddr))); - if (ipm->peer) /* Check if a peer protocol is defined */ + if (ipm->peer) { /* Check if a peer protocol is defined */ gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len); + return 0; + } + + if (iph->version != 4) + return 0; + + LOGTUN(LOGL_DEBUG, tun, "No PDP Context for Destination Address, send host unreach.\n"); + ipv4_host_unreach(tun, pack, len); return 0; + } /* MS-originated GTP1-U packet, needs to be sent via TUN device */ -- cgit v1.2.3