[PATCH 14/16] gtp-rtnl: Split tid handling for GTPv0 and GTPv1

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/OpenBSC@lists.osmocom.org/.

Neels Hofmeyr nhofmeyr at sysmocom.de
Thu Nov 19 14:00:02 UTC 2015


On Mon, Nov 16, 2015 at 04:06:55PM +0100, Andreas Schultz wrote:
> GTPv1 tunnel use a separate 32bit TIDs for each direction while GTPv0 uses
> only one 64bit TID.

English nitpicking: "GTPv1 tunnels use separate..."

...and the names differ. In v0, it's called "Tunnel IDentifier"; in v1
they're two "Tunnel Endpoint Identifier"s ("TID" vs. "TEI"). This naming
is used consistently in the specs.

> --- a/libgtnl/include/libgtpnl/gtp.h
> +++ b/libgtnl/include/libgtpnl/gtp.h
> @@ -14,6 +14,8 @@ void gtp_tunnel_set_ms_ip4(struct gtp_tunnel *t, struct in_addr *ms_addr);
>  void gtp_tunnel_set_sgsn_ip4(struct gtp_tunnel *t, struct in_addr *sgsn_addr);
>  void gtp_tunnel_set_version(struct gtp_tunnel *t, uint32_t version);
>  void gtp_tunnel_set_tid(struct gtp_tunnel *t, uint64_t tid);
> +void gtp_tunnel_set_i_tid(struct gtp_tunnel *t, uint32_t i_tid);
> +void gtp_tunnel_set_o_tid(struct gtp_tunnel *t, uint32_t o_tid);
[...]
> +uint32_t gtp_tunnel_get_i_tid(struct gtp_tunnel *t);
> +uint32_t gtp_tunnel_get_o_tid(struct gtp_tunnel *t);
[...]
> +	GTPA_I_TID,
> +	GTPA_O_TID,
[...]
> +	union {
    [...]
> +		struct {
> +			uint32_t i_tid;
> +			uint32_t o_tid;
> +		} v1;
> +	} u;
[...]
> +	else if (pdp.version == GTP_V1)
> +		printf("tid %u/%u ms_addr %s ", pdp.u.v1.i_tid, pdp.u.v1.o_tid, inet_ntoa(pdp.sgsn_addr));

So I'd call all of these "tei" and "TEI" (leaving v0 ones called "tid").

 
> +static void add_usage(const char *name)

"add" to stdout? :)
Ah I see, print the usage for the "add" command....

> +{
> +	printf("%s add <gtp device> <v0> <tid> <ms-addr> <sgsn-addr>\n",
> +	       name);
> +	printf("%s add <gtp device> <v1> <i_tid> <o_tid> <ms-addr> <sgsn-addr>\n",
> +	       name);
> +}

"v0" and "v1" are the exact literal arguments to pass, in which case I
would omit the braces, like
  printf("%s add <gtp device> v1 <i_tid> <o_tid> <ms-addr> <sgsn-addr>\n",

> +	uint32_t gtp_version;
In the GTP headers, there are just three bits available for indicating the
GTP version. Any particular reason to pick a uint32_t?
 
> +	t = gtp_tunnel_alloc();
[...]
> +	gtp_tunnel_free(t);

It seems that you calloc and free a struct gtp_tunnel within the same
function (I think twice). You could use a local struct instead?
  struct gtp_tunnel = {0};

And a possible mem leak because of that:

>  static int
>  add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
>  {
> +	struct gtp_tunnel *t;
[...]
> +	t = gtp_tunnel_alloc();
> +	optidx = 2;
> +
> +	gtp_ifidx = if_nametoindex(argv[optidx]);
>  	if (gtp_ifidx == 0) {
> -		fprintf(stderr, "wrong GTP interface %s\n", argv[2]);
> +		fprintf(stderr, "wrong GTP interface %s\n", argv[optidx]);
>  		return EXIT_FAILURE;

t is still allocated upon EXIT_FAILURE.

>  	}
> +	gtp_tunnel_set_ifidx(t, gtp_ifidx);
>  
> -	if (inet_aton(argv[5], &ms) < 0) {
> -		perror("bad address for ms");
> -		exit(EXIT_FAILURE);
> -	}
> -
> -	if (inet_aton(argv[6], &sgsn) < 0) {
> -		perror("bad address for sgsn");
> -		exit(EXIT_FAILURE);
> -	}
> +	optidx++;
>  
> -	if (strcmp(argv[3], "v0") == 0)
> +	if (strcmp(argv[optidx], "v0") == 0)
>  		gtp_version = GTP_V0;
> -	else if (strcmp(argv[3], "v1") == 0)
> +	else if (strcmp(argv[optidx], "v1") == 0)
>  		gtp_version = GTP_V1;
>  	else {
>  		fprintf(stderr, "wrong GTP version %s, use v0 or v1\n",
> -			argv[3]);
> +			argv[optidx]);
>  		return EXIT_FAILURE;

t is still allocated upon EXIT_FAILURE.

>  	}
> +	gtp_tunnel_set_version(t, gtp_version);
>  
> -	nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_EXCL | NLM_F_ACK, ++seq,
> -				   GTP_CMD_TUNNEL_NEW);
> -	gtp_build_payload(nlh, atoi(argv[4]), gtp_ifidx, sgsn.s_addr,
> -			  ms.s_addr, gtp_version);
> +	if (gtp_version == GTP_V0)
> +		gtp_tunnel_set_tid(t, atoi(argv[optidx++]));
> +	else if (gtp_version == GTP_V1) {
> +		gtp_tunnel_set_i_tid(t, atoi(argv[optidx++]));
> +		gtp_tunnel_set_o_tid(t, atoi(argv[optidx++]));
> +	}
>  
> -	if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
> -		perror("genl_socket_talk");
> +	if (inet_aton(argv[optidx++], &ms) < 0) {
> +		perror("bad address for ms");
> +		exit(EXIT_FAILURE);

Some failures return EXIT_FAILURE, some exit() directly? Is that intended?

> +	}
> +	gtp_tunnel_set_ms_ip4(t, &ms);
> +
> +	if (inet_aton(argv[optidx++], &sgsn) < 0) {
> +		perror("bad address for sgsn");
> +		exit(EXIT_FAILURE);
> +	}
> +	gtp_tunnel_set_sgsn_ip4(t, &sgsn);
> +
> +	gtp_add_tunnel(genl_id, nl, t);
>  
> +	gtp_tunnel_free(t);

Just to make sure ... t's data is copied by gtp_add_tunnel(), right?
All in all, seems to be a case for a local struct.
Same thing in del_tunnel():

>  	return 0;
>  }
>  
>  static int
>  del_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
>  {
> +	struct gtp_tunnel *t;
[...]
> +	t = gtp_tunnel_alloc();
>  
> -	nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_ACK, ++seq,
> -				   GTP_CMD_TUNNEL_DELETE);
> -	gtp_build_payload(nlh, atoi(argv[4]), gtp_ifidx, 0, 0, atoi(argv[3]));
> +	gtp_ifidx = if_nametoindex(argv[2]);
> +	if (gtp_ifidx == 0) {
> +		fprintf(stderr, "wrong GTP interface %s\n", argv[2]);
> +		return EXIT_FAILURE;

possible missing free

> +	}
> +	gtp_tunnel_set_ifidx(t, gtp_ifidx);
> +
> +	if (strcmp(argv[3], "v0") == 0) {
> +		gtp_tunnel_set_version(t, GTP_V0);
> +		gtp_tunnel_set_tid(t, atoi(argv[4]));
> +	} else if (strcmp(argv[3], "v1") == 0) {
> +		gtp_tunnel_set_version(t, GTP_V1);
> +		gtp_tunnel_set_i_tid(t, atoi(argv[4]));
> +	} else {
> +		fprintf(stderr, "wrong GTP version %s, use v0 or v1\n",
> +			argv[3]);
> +		return EXIT_FAILURE;

possible missing free

> +	}
>  
> -	if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
> -		perror("genl_socket_talk");
> +	gtp_del_tunnel(genl_id, nl, t);
>  
> +	gtp_tunnel_free(t);

...same as above.


>  struct gtp_pdp {
>  	uint32_t	version;
> -	uint64_t	tid;
> +	union {
> +		struct {
> +			uint64_t tid;
> +		} v0;
> +		struct {
> +			uint32_t i_tid;
> +			uint32_t o_tid;
> +		} v1;
> +	} u;

The same union again? Actually, it seems the entire struct gtp_pdp
definition is duplicated. Wouldn't it be better to define it once and
reuse?

> @@ -152,12 +197,16 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
>  static int genl_gtp_attr_cb(const struct nlmsghdr *nlh, void *data)
>  {
>  	struct nlattr *tb[GTPA_MAX + 1] = {};
> -	struct gtp_pdp pdp;
> +	struct gtp_pdp pdp = {};

Heh, exactly :)

Actually, we've recently had a discussion in the sysmocom office about
this way of zero initialization. There's the "traditional" method of
naming one element as zero, after which the remaining elements will also
be initialized to nul. If the first member is a primitive, {0} suffices.
If not, one has to explicitly name a member, which can be cumbersome. Then
I came across the possibility to just omit all members and obviously
preferred that: {}. However, that doesn't seem to be part of C99, so now
I'm back to naming a member again.  Any further insights on the subject
would be appreciated...

~Neels

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.osmocom.org/pipermail/openbsc/attachments/20151119/9966a5a0/attachment.bin>


More information about the OpenBSC mailing list