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
This commit is contained in:
Keith Whyte 2020-10-20 08:40:50 +02:00
parent 1719abb409
commit 48f2343310
1 changed files with 84 additions and 5 deletions

View File

@ -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 */