Change in osmo-ggsn[master]: sgsnemu: Implement ping on IPv6 APNs

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Wed Apr 15 14:42:26 UTC 2020


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/17826 )


Change subject: sgsnemu: Implement ping on IPv6 APNs
......................................................................

sgsnemu: Implement ping on IPv6 APNs

Related: OS#4434
Change-Id: If9ca7c37a1a397bbc3f8912d67bccdabc4968e0c
---
M lib/icmpv6.c
M lib/icmpv6.h
M sgsnemu/sgsnemu.c
3 files changed, 234 insertions(+), 46 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ggsn refs/changes/26/17826/1

diff --git a/lib/icmpv6.c b/lib/icmpv6.c
index 1bddf65..ac1474d 100644
--- a/lib/icmpv6.c
+++ b/lib/icmpv6.c
@@ -42,7 +42,7 @@
 };
 
 /* Prepends the ipv6 header and returns checksum content */
-static uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
+uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
 				  const struct in6_addr *daddr)
 {
 	uint32_t len;
diff --git a/lib/icmpv6.h b/lib/icmpv6.h
index 44b9b73..4b4dc37 100644
--- a/lib/icmpv6.h
+++ b/lib/icmpv6.h
@@ -22,6 +22,13 @@
 	uint16_t csum;
 } __attribute__ ((packed));
 
+struct icmpv6_echo_hdr {
+	struct icmpv6_hdr hdr;
+	uint16_t ident;		/* Identifier */
+	uint16_t seq;		/* Sequence number */
+	uint8_t data[0];	/* Data */
+} __attribute__ ((packed));
+
 /* RFC4861 Section 4.1 */
 struct icmpv6_rsol_hdr {
 	struct icmpv6_hdr hdr;
@@ -76,6 +83,9 @@
 	uint8_t prefix[16];
 } __attribute__ ((packed));
 
+uint16_t icmpv6_prepend_ip6hdr(struct msgb *msg, const struct in6_addr *saddr,
+				  const struct in6_addr *daddr);
+
 struct msgb *icmpv6_construct_rs(const struct in6_addr *saddr);
 
 int handle_router_mcast(struct gsn_t *gsn, struct pdp_t *pdp,
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index f3bf848..779c887 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -101,7 +101,7 @@
 	size_t prefixlen;
 	char *ipup, *ipdown;	/* Filename of scripts */
 	int defaultroute;	/* Set up default route */
-	struct in_addr pinghost;	/* Remote ping host    */
+	struct in46_addr pinghost;	/* Remote ping host    */
 	int pingrate;
 	int pingsize;
 	int pingcount;
@@ -163,6 +163,11 @@
 	uint8_t data[CREATEPING_MAX];	/* Data */
 } __attribute__ ((packed));
 
+struct ip6_ping {
+	struct icmpv6_echo_hdr hdr;
+	uint8_t data[CREATEPING_MAX];	/* Data */
+} __attribute__ ((packed));
+
 /* Statistical values for ping */
 int nreceived = 0;
 int ntreceived = 0;
@@ -913,20 +918,61 @@
 	/* defaultroute */
 	options.defaultroute = args_info.defaultroute_flag;
 
+	/* PDP Type */
+	if (!strcmp(args_info.pdp_type_arg, "v6"))
+		options.pdp_type = PDP_EUA_TYPE_v6;
+	else if (!strcmp(args_info.pdp_type_arg, "v4"))
+		options.pdp_type = PDP_EUA_TYPE_v4;
+	else {
+		SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unsupported/unknown PDP Type '%s'\n",
+			args_info.pdp_type_arg);
+		return -1;
+	}
+
 	/* pinghost                                                     */
 	/* Store ping host as in_addr                                   */
 	if (args_info.pinghost_arg) {
-		if (!(host = gethostbyname(args_info.pinghost_arg))) {
+		struct addrinfo hints;
+		struct addrinfo *result;
+		memset(&hints, 0, sizeof(struct addrinfo));
+		switch (options.pdp_type) {
+		case PDP_EUA_TYPE_v4:
+			hints.ai_family = AF_INET;
+			break;
+		case PDP_EUA_TYPE_v6:
+			hints.ai_family = AF_INET6;
+			break;
+		default:
+			SYS_ERR(DSGSN, LOGL_ERROR, 0, "lookup(AF_UNSPEC) %d", options.pdp_type);
+			hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+		}
+		hints.ai_socktype = SOCK_DGRAM;
+		hints.ai_flags = 0;
+		hints.ai_protocol = 0;
+		hints.ai_canonname = NULL;
+		hints.ai_addr = NULL;
+		hints.ai_next = NULL;
+		if ((i = getaddrinfo(args_info.pinghost_arg, NULL, &hints, &result)) != 0) {
 			SYS_ERR(DSGSN, LOGL_ERROR, 0,
-				"Invalid ping host: %s!",
-				args_info.pinghost_arg);
+				"Invalid ping host '%s': %s",
+				args_info.pinghost_arg, gai_strerror(i));
 			return -1;
 		} else {
-			memcpy(&options.pinghost.s_addr, host->h_addr,
-			       host->h_length);
+			switch (result->ai_family) {
+			case AF_INET:
+				options.pinghost.len = sizeof(struct in_addr);
+				options.pinghost.v4 = ((struct sockaddr_in*)result->ai_addr)->sin_addr;
+				SYS_ERR(DSGSN, LOGL_ERROR, 0, 	"AF_INET %d", options.pinghost.len);
+				break;
+			case AF_INET6:
+				options.pinghost.len = sizeof(struct in6_addr);
+				options.pinghost.v6 = ((struct sockaddr_in6*)result->ai_addr)->sin6_addr;
+				break;
+			}
 			printf("Using ping host:       %s (%s)\n",
 			       args_info.pinghost_arg,
-			       inet_ntoa(options.pinghost));
+			       in46a_ntoa(&options.pinghost));
+			freeaddrinfo(result);
 		}
 	}
 
@@ -944,17 +990,6 @@
 	else
 		options.tx_gpdu_seq = 1;
 
-	/* PDP Type */
-	if (!strcmp(args_info.pdp_type_arg, "v6"))
-		options.pdp_type = PDP_EUA_TYPE_v6;
-	else if (!strcmp(args_info.pdp_type_arg, "v4"))
-		options.pdp_type = PDP_EUA_TYPE_v4;
-	else {
-		SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unsupported/unknown PDP Type '%s'\n",
-			args_info.pdp_type_arg);
-		return -1;
-	}
-
 	if (options.pingcount && options.pdp_type != PDP_EUA_TYPE_v4) {
 		SYS_ERR(DSGSN, LOGL_ERROR, 0, "built-in ping only works with IPv4, use tun-device");
 		return -1;
@@ -1115,7 +1150,7 @@
 	struct timezone tz;
 	struct timeval tv;
 	int diff;
-	if ((options.pinghost.s_addr) && (2 == state) &&
+	if ((options.pinghost.len) && (2 == state) &&
 	    ((pingseq < options.pingcount) || (options.pingcount == 0))) {
 		gettimeofday(&tv, &tz);
 		diff = 1000000 / options.pingrate * pingseq - 1000000 * (tv.tv_sec - firstping.tv_sec) - (tv.tv_usec - firstping.tv_usec);	/* Microseconds safe up to 500 sec */
@@ -1140,7 +1175,7 @@
 	gettimeofday(&tv, &tz);
 	elapsed = 1000000 * (tv.tv_sec - firstping.tv_sec) + (tv.tv_usec - firstping.tv_usec);	/* Microseconds */
 	printf("\n");
-	printf("\n----%s PING Statistics----\n", inet_ntoa(options.pinghost));
+	printf("\n----%s PING Statistics----\n", in46a_ntoa(&options.pinghost));
 	printf("%d packets transmitted in %.3f seconds, ", ntransmitted,
 	       elapsed / 1000000.0);
 	printf("%d packets received, ", nreceived);
@@ -1164,10 +1199,8 @@
 	return 0;
 }
 
-/* Handle a received ping packet. Print out line and update statistics. */
-static int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
+static int encaps_ping4(struct pdp_t *pdp, void *pack, unsigned len)
 {
-	struct timezone tz;
 	struct timeval tv;
 	struct timeval *tp;
 	struct ip_ping *pingpack = pack;
@@ -1176,17 +1209,12 @@
 
 	src.s_addr = pingpack->src;
 
-	gettimeofday(&tv, &tz);
-	if (options.debug)
-		printf("%d.%6d ", (int)tv.tv_sec, (int)tv.tv_usec);
-
 	if (len < CREATEPING_IP + CREATEPING_ICMP) {
 		printf("packet too short (%d bytes) from %s\n", len,
 		       inet_ntoa(src));
 		return 0;
 	}
 
-	ntreceived++;
 	if (pingpack->protocol != 1) {
 		if (!options.pingquiet)
 			printf("%d bytes from %s: ip_protocol=%d (%s)\n",
@@ -1210,7 +1238,7 @@
 		       inet_ntoa(src), ntohs(pingpack->seq));
 
 	if (len >= sizeof(struct timeval) + CREATEPING_IP + CREATEPING_ICMP) {
-		gettimeofday(&tv, &tz);
+		gettimeofday(&tv, NULL);
 		tp = (struct timeval *)pingpack->data;
 		if ((tv.tv_usec -= tp->tv_usec) < 0) {
 			tv.tv_sec--;
@@ -1233,15 +1261,106 @@
 	return 0;
 }
 
-/* Create a new ping packet and send it off to peer. */
-static int create_ping(void *gsn, struct pdp_t *pdp,
-			struct in_addr *dst, int seq, unsigned int datasize)
+static int encaps_ping6(struct pdp_t *pdp, struct ip6_hdr *ip6h, unsigned len)
 {
+	const struct icmpv6_echo_hdr *ic6h = (struct icmpv6_echo_hdr *) ((uint8_t*)ip6h + sizeof(*ip6h));
+	struct timeval tv;
+	struct timeval tp;
+	int triptime;
+	char straddr[128];
 
+	if (len < sizeof(struct ip6_hdr)) {
+		SYS_ERR(DSGSN, LOGL_ERROR, 0, "Packet len too small to contain IPv6 header (%d)", len);
+		return 0;
+	}
+
+	if (ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_ICMPV6) {
+		if (!options.pingquiet)
+			printf("%d bytes from %s: ip6_protocol=%d (%s)\n", len,
+			       inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)),
+			       ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt,
+			       print_ipprot(ip6h->ip6_ctlun.ip6_un1.ip6_un1_nxt));
+		return 0;
+	}
+
+	if (len < sizeof(struct ip6_hdr) + sizeof(struct icmpv6_echo_hdr)) {
+		LOGP(DSGSN, LOGL_ERROR, "Packet len too small to contain ICMPv6 echo header (%d)\n", len);
+		return 0;
+	}
+
+	if (ic6h->hdr.type != 129 || ic6h->hdr.code != 0) {
+		if (!options.pingquiet)
+			printf
+			    ("%d bytes from %s: icmp_type=%d icmp_code=%d\n", len,
+			    inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)),
+			    ic6h->hdr.type, ic6h->hdr.code);
+		return 0;
+	}
+
+	nreceived++;
+	if (!options.pingquiet)
+		printf("%d bytes from %s: icmp_seq=%d", len,
+		       inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)),
+		       ntohs(ic6h->seq));
+
+	if (len >= sizeof(struct ip6_hdr) + sizeof(struct icmpv6_echo_hdr) + sizeof(struct timeval)) {
+		gettimeofday(&tv, NULL);
+		memcpy(&tp, ic6h->data, sizeof(struct timeval));
+		if ((tv.tv_usec -= tp.tv_usec) < 0) {
+			tv.tv_sec--;
+			tv.tv_usec += 1000000;
+		}
+		tv.tv_sec -= tp.tv_sec;
+
+		triptime = tv.tv_sec * 1000000 + (tv.tv_usec);
+		tsum += triptime;
+		if (triptime < tmin)
+			tmin = triptime;
+		if (triptime > tmax)
+			tmax = triptime;
+
+		if (!options.pingquiet)
+			printf(" time=%.3f ms\n", triptime / 1000.0);
+
+	} else if (!options.pingquiet)
+		printf("\n");
+	return 0;
+}
+
+/* Handle a received ping packet. Print out line and update statistics. */
+static int encaps_ping(struct pdp_t *pdp, void *pack, unsigned len)
+{
+	struct iphdr *iph = (struct iphdr *)pack;
+	struct timeval tv;
+
+
+	gettimeofday(&tv, NULL);
+	if (options.debug)
+		printf("%d.%6d ", (int)tv.tv_sec, (int)tv.tv_usec);
+
+	ntreceived++;
+
+	if (len < sizeof(struct iphdr)) {
+		SYS_ERR(DSGSN, LOGL_ERROR, 0, "Packet len too small to contain ip header (%d)", len);
+		return -1;
+	}
+	switch(iph->version) {
+	case 4:
+		return encaps_ping4(pdp, pack, len);
+	case 6:
+		return encaps_ping6(pdp, (struct ip6_hdr *)pack, len);
+	default:
+		SYS_ERR(DSGSN, LOGL_ERROR, 0, "Unknown ip header version %d", iph->version);
+		return -1;
+	}
+}
+
+static int create_ping4(void *gsn, struct pdp_t *pdp, struct in46_addr *src,
+			struct in46_addr *dst, int seq, unsigned int datasize)
+{
 	struct ip_ping pack;
 	uint16_t v16;
 	uint8_t *p8 = (uint8_t *) & pack;
-	struct in_addr src;
 	unsigned int n;
 	long int sum = 0;
 	int count = 0;
@@ -1250,14 +1369,6 @@
 	struct timeval *tp =
 	    (struct timeval *)&p8[CREATEPING_IP + CREATEPING_ICMP];
 
-	if (datasize > CREATEPING_MAX) {
-		SYS_ERR(DSGSN, LOGL_ERROR, 0,
-			"Ping size to large: %d!", datasize);
-		return -1;
-	}
-
-	memcpy(&src, &(pdp->eua.v[2]), 4);	/* Copy a 4 byte address */
-
 	pack.ipver = 0x45;
 	pack.tos = 0x00;
 	pack.length = htons(CREATEPING_IP + CREATEPING_ICMP + datasize);
@@ -1266,8 +1377,8 @@
 	pack.ttl = 0x40;
 	pack.protocol = 0x01;
 	pack.ipcheck = 0x0000;
-	pack.src = src.s_addr;
-	pack.dst = dst->s_addr;
+	pack.src = src->v4.s_addr;
+	pack.dst = dst->v4.s_addr;
 	pack.type = 0x08;
 	pack.code = 0x00;
 	pack.checksum = 0x0000;
@@ -1317,6 +1428,73 @@
 	return gtp_data_req(gsn, pdp, &pack, 28 + datasize);
 }
 
+static int create_ping6(void *gsn, struct pdp_t *pdp, struct in46_addr *src,
+			struct in46_addr *dst, int seq, unsigned int datasize)
+{
+	struct ip6_ping *pack;
+	uint8_t *p8;
+	unsigned int n;
+	struct timezone tz;
+	struct timeval *tp;
+
+	struct msgb *msg = msgb_alloc_headroom(sizeof(struct ip6_ping) + 128,128, "ICMPv6 echo");
+	OSMO_ASSERT(msg);
+	pack = (struct ip6_ping *) msgb_put(msg, sizeof(struct icmpv6_echo_hdr) + datasize);
+	pack->hdr.hdr.type = 128;
+	pack->hdr.hdr.code = 0;
+	pack->hdr.hdr.csum = 0;  /* updated below */
+	pack->hdr.ident = 0x0000;
+	pack->hdr.seq = htons(seq);
+
+	p8 = pack->data;
+	for (n = 0; n < (datasize); n++)
+		p8[n] = n;
+
+	if (datasize >= sizeof(struct timeval)) {
+		tp = (struct timeval *)pack->data;
+		gettimeofday(tp, &tz);
+	}
+
+	pack->hdr.hdr.csum = icmpv6_prepend_ip6hdr(msg, &src->v6, &dst->v6);
+
+	ntransmitted++;
+	return gtp_data_req(gsn, pdp, msgb_data(msg), msgb_length(msg));
+}
+
+/* Create a new ping packet and send it off to peer. */
+static int create_ping(void *gsn, struct pdp_t *pdp,
+			struct in46_addr *dst, int seq, unsigned int datasize)
+{
+	int num_addr;
+	struct in46_addr addr[2];
+	struct in46_addr *src;
+
+	if (datasize > CREATEPING_MAX) {
+		SYS_ERR(DSGSN, LOGL_ERROR, 0,
+			"Ping size to large: %d!", datasize);
+		return -1;
+	}
+
+	if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 1) {
+		SYS_ERR(DSGSN, LOGL_ERROR, 0,
+			"in46a_from_eua() failed! %d", num_addr);
+		return -1;
+	}
+	if (dst->len == addr[0].len) {
+		src = &addr[0];
+	} else if (num_addr > 1 && dst->len == addr[1].len) {
+		src = &addr[1];
+	} else {
+		SYS_ERR(DSGSN, LOGL_ERROR, 0,
+			"Mismaching source and destination IP addr types (%d vs %d)", dst->len, addr[0].len);
+		return -1;
+	}
+	if (in46a_is_v4(dst))
+		return create_ping4(gsn, pdp, src, dst, seq, datasize);
+	else
+		return create_ping6(gsn, pdp, src, dst, seq, datasize);
+}
+
 static int delete_context(struct pdp_t *pdp)
 {
 	int rc;
@@ -1949,7 +2127,7 @@
 				/* Delete context */
 				printf("Disconnecting PDP context #%d\n", n);
 				gtp_delete_context_req2(gsn, iparr[n].pdp, NULL, 1);
-				if ((options.pinghost.s_addr != 0)
+				if ((options.pinghost.len)
 				    && ntransmitted)
 					ping_finish();
 			}
@@ -1959,7 +2137,7 @@
 		diff = 0;
 		while ((diff <= 0) &&
 		       /* Send off an ICMP ping packet */
-		       /*if ( */ (options.pinghost.s_addr) && (2 == state) &&
+		       /*if ( */ (options.pinghost.len) && (2 == state) &&
 		       ((pingseq < options.pingcount)
 			|| (options.pingcount == 0))) {
 			if (!pingseq)

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ggsn/+/17826
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: If9ca7c37a1a397bbc3f8912d67bccdabc4968e0c
Gerrit-Change-Number: 17826
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200415/f59d7c07/attachment.htm>


More information about the gerrit-log mailing list