aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ggsn/ggsn.c89
1 files 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 <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
#include <netinet/ip6.h>
#include <osmocom/core/timer.h>
@@ -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 */