Add IPv6 support
Implement IPv6 in libgtpnl and the gtp-tunnel testing tool. Allow to combine: - GTPA_MS_ADDRESS and GTPA_PEER_ADDR6 - GTPA_MS_ADDR6 and GTPA_PEER_ADDRESS to specify IPv4-in-IPv6-GTP and IPv6-in-IPv4-GTP in the tunnel declaration from control plane. This patch is based on multiple patches from Pablo in OS#6123. I decided to squash them to directly implement v4-in-v6 and vice versa, instead of implementing another variant first and then changing it again. Co-developed-by: Pablo Neira Ayuso <pablo@netfilter.org> Related: OS#6096 Change-Id: If864c9170f74af52a95cbc4cdb1b866e0309306b
This commit is contained in:
parent
717db098c7
commit
a295615229
|
@ -13,6 +13,8 @@ void gtp_tunnel_set_ifns(struct gtp_tunnel *t, int ifns);
|
|||
void gtp_tunnel_set_ifidx(struct gtp_tunnel *t, uint32_t ifidx);
|
||||
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_ms_ip6(struct gtp_tunnel *t, const struct in6_addr *ms_addr);
|
||||
void gtp_tunnel_set_sgsn_ip6(struct gtp_tunnel *t, const struct in6_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_tei(struct gtp_tunnel *t, uint32_t i_tei);
|
||||
|
|
|
@ -31,6 +31,9 @@ enum gtp_attrs {
|
|||
GTPA_I_TEI, /* for GTPv1 only */
|
||||
GTPA_O_TEI, /* for GTPv1 only */
|
||||
GTPA_PAD,
|
||||
GTPA_PEER_ADDR6,
|
||||
GTPA_MS_ADDR6,
|
||||
GTPA_FAMILY,
|
||||
__GTPA_MAX,
|
||||
};
|
||||
#define GTPA_MAX (__GTPA_MAX - 1)
|
||||
|
|
|
@ -44,14 +44,36 @@
|
|||
|
||||
static void gtp_build_payload(struct nlmsghdr *nlh, struct gtp_tunnel *t)
|
||||
{
|
||||
mnl_attr_put_u8(nlh, GTPA_FAMILY, t->ms_addr.family);
|
||||
mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version);
|
||||
if (t->ifns >= 0)
|
||||
mnl_attr_put_u32(nlh, GTPA_NET_NS_FD, t->ifns);
|
||||
mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx);
|
||||
if (t->sgsn_addr.ip4.s_addr)
|
||||
mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.ip4.s_addr);
|
||||
if (t->ms_addr.ip4.s_addr)
|
||||
|
||||
switch (t->ms_addr.family) {
|
||||
case AF_INET:
|
||||
mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, t->ms_addr.ip4.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
mnl_attr_put(nlh, GTPA_MS_ADDR6, sizeof(t->ms_addr.ip6), &t->ms_addr.ip6);
|
||||
break;
|
||||
default:
|
||||
/* No addr is set when deleting a tunnel */
|
||||
break;
|
||||
}
|
||||
|
||||
switch (t->sgsn_addr.family) {
|
||||
case AF_INET:
|
||||
mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.ip4.s_addr);
|
||||
break;
|
||||
case AF_INET6:
|
||||
mnl_attr_put(nlh, GTPA_PEER_ADDR6, sizeof(t->sgsn_addr.ip6), &t->sgsn_addr.ip6);
|
||||
break;
|
||||
default:
|
||||
/* No addr is set when deleting a tunnel */
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->gtp_version == GTP_V0) {
|
||||
mnl_attr_put_u64(nlh, GTPA_TID, t->u.v0.tid);
|
||||
mnl_attr_put_u16(nlh, GTPA_FLOW, t->u.v0.flowid);
|
||||
|
@ -129,6 +151,12 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
|
|||
return MNL_CB_OK;
|
||||
|
||||
switch(type) {
|
||||
case GTPA_FAMILY:
|
||||
if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
|
||||
perror("mnl_attr_validate");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
break;
|
||||
case GTPA_TID:
|
||||
if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
|
||||
perror("mnl_attr_validate");
|
||||
|
@ -145,6 +173,14 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
|
|||
return MNL_CB_ERROR;
|
||||
}
|
||||
break;
|
||||
case GTPA_PEER_ADDR6:
|
||||
case GTPA_MS_ADDR6:
|
||||
if (mnl_attr_validate2(attr, MNL_TYPE_BINARY,
|
||||
sizeof(struct in6_addr)) < 0) {
|
||||
perror("mnl_attr_validate");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -160,36 +196,56 @@ static int genl_gtp_attr_cb(const struct nlmsghdr *nlh, void *data)
|
|||
struct genlmsghdr *genl;
|
||||
|
||||
mnl_attr_parse(nlh, sizeof(*genl), genl_gtp_validate_cb, tb);
|
||||
|
||||
if (tb[GTPA_TID])
|
||||
pdp.u.v0.tid = mnl_attr_get_u64(tb[GTPA_TID]);
|
||||
if (tb[GTPA_I_TEI])
|
||||
pdp.u.v1.i_tei = mnl_attr_get_u32(tb[GTPA_I_TEI]);
|
||||
if (tb[GTPA_O_TEI])
|
||||
pdp.u.v1.o_tei = mnl_attr_get_u32(tb[GTPA_O_TEI]);
|
||||
|
||||
if (tb[GTPA_PEER_ADDRESS]) {
|
||||
pdp.sgsn_addr.family = AF_INET;
|
||||
pdp.sgsn_addr.ip4.s_addr =
|
||||
mnl_attr_get_u32(tb[GTPA_PEER_ADDRESS]);
|
||||
pdp.sgsn_addr.ip4.s_addr = mnl_attr_get_u32(tb[GTPA_PEER_ADDRESS]);
|
||||
} else if (tb[GTPA_PEER_ADDR6]) {
|
||||
pdp.sgsn_addr.family = AF_INET6;
|
||||
memcpy(&pdp.sgsn_addr.ip6, mnl_attr_get_payload(tb[GTPA_PEER_ADDR6]),
|
||||
sizeof(struct in6_addr));
|
||||
} else {
|
||||
fprintf(stderr, "sgsn_addr: no IPv4 nor IPv6 set\n");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
if (tb[GTPA_MS_ADDRESS]) {
|
||||
pdp.ms_addr.family = AF_INET;
|
||||
pdp.ms_addr.ip4.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]);
|
||||
}
|
||||
if (tb[GTPA_VERSION]) {
|
||||
pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]);
|
||||
} else if (tb[GTPA_MS_ADDR6]) {
|
||||
pdp.ms_addr.family = AF_INET6;
|
||||
memcpy(&pdp.ms_addr.ip6, mnl_attr_get_payload(tb[GTPA_MS_ADDR6]), sizeof(struct in6_addr));
|
||||
} else {
|
||||
fprintf(stderr, "ms_addr: no IPv4 nor IPv6 set\n");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
printf("version %u ", pdp.version);
|
||||
if (pdp.version == GTP_V0) {
|
||||
inet_ntop(AF_INET, &pdp.ms_addr.ip4, buf, sizeof(buf));
|
||||
printf("tid %"PRIu64" ms_addr %s ",
|
||||
pdp.u.v0.tid, buf);
|
||||
} else if (pdp.version == GTP_V1) {
|
||||
inet_ntop(AF_INET, &pdp.ms_addr.ip4, buf, sizeof(buf));
|
||||
printf("tei %u/%u ms_addr %s ", pdp.u.v1.i_tei,
|
||||
pdp.u.v1.o_tei, buf);
|
||||
if (tb[GTPA_FAMILY] && mnl_attr_get_u32(tb[GTPA_FAMILY]) != pdp.ms_addr.family) {
|
||||
fprintf(stderr, "ms_addr family does not match GTPA_FAMILY\n");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
inet_ntop(AF_INET, &pdp.sgsn_addr.ip4, buf, sizeof(buf));
|
||||
|
||||
if (tb[GTPA_VERSION])
|
||||
pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]);
|
||||
|
||||
printf("version %u ", pdp.version);
|
||||
|
||||
if (pdp.version == GTP_V0)
|
||||
printf("tid %"PRIu64" ", pdp.u.v0.tid);
|
||||
else if (pdp.version == GTP_V1)
|
||||
printf("tei %u/%u ", pdp.u.v1.i_tei, pdp.u.v1.o_tei);
|
||||
|
||||
inet_ntop(pdp.ms_addr.family, &pdp.ms_addr.ip4, buf, sizeof(buf));
|
||||
printf("ms_addr %s ", buf);
|
||||
|
||||
inet_ntop(pdp.sgsn_addr.family, &pdp.sgsn_addr.ip4, buf, sizeof(buf));
|
||||
printf("sgsn_addr %s\n", buf);
|
||||
|
||||
return MNL_CB_OK;
|
||||
|
|
14
src/gtp.c
14
src/gtp.c
|
@ -72,6 +72,20 @@ void gtp_tunnel_set_sgsn_ip4(struct gtp_tunnel *t, struct in_addr *sgsn_addr)
|
|||
}
|
||||
EXPORT_SYMBOL(gtp_tunnel_set_sgsn_ip4);
|
||||
|
||||
void gtp_tunnel_set_ms_ip6(struct gtp_tunnel *t, const struct in6_addr *ms_addr)
|
||||
{
|
||||
t->ms_addr.family = AF_INET6;
|
||||
t->ms_addr.ip6 = *ms_addr;
|
||||
}
|
||||
EXPORT_SYMBOL(gtp_tunnel_set_ms_ip6);
|
||||
|
||||
void gtp_tunnel_set_sgsn_ip6(struct gtp_tunnel *t, const struct in6_addr *sgsn_addr)
|
||||
{
|
||||
t->sgsn_addr.family = AF_INET6;
|
||||
t->sgsn_addr.ip6 = *sgsn_addr;
|
||||
}
|
||||
EXPORT_SYMBOL(gtp_tunnel_set_sgsn_ip6);
|
||||
|
||||
void gtp_tunnel_set_version(struct gtp_tunnel *t, uint32_t version)
|
||||
{
|
||||
t->gtp_version = version;
|
||||
|
|
|
@ -38,3 +38,8 @@ global:
|
|||
|
||||
local: *;
|
||||
};
|
||||
|
||||
LIBGTPNL_1.1 {
|
||||
gtp_tunnel_set_ms_ip6;
|
||||
gtp_tunnel_set_sgsn_ip6;
|
||||
} LIBGTPNL_1.0;
|
||||
|
|
|
@ -49,12 +49,32 @@ static void add_usage(const char *name)
|
|||
name);
|
||||
}
|
||||
|
||||
static void set_addr(const char *addr, bool is_ms, struct gtp_tunnel *t)
|
||||
{
|
||||
struct in_addr ip4;
|
||||
struct in6_addr ip6;
|
||||
|
||||
if (inet_pton(AF_INET, addr, &ip4) == 1) {
|
||||
if (is_ms)
|
||||
return gtp_tunnel_set_ms_ip4(t, &ip4);
|
||||
return gtp_tunnel_set_sgsn_ip4(t, &ip4);
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET6, addr, &ip6) == 1) {
|
||||
if (is_ms)
|
||||
return gtp_tunnel_set_ms_ip6(t, &ip6);
|
||||
return gtp_tunnel_set_sgsn_ip6(t, &ip6);
|
||||
}
|
||||
|
||||
fprintf(stderr, "bad address: %s\n", addr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static int
|
||||
add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
|
||||
{
|
||||
struct gtp_tunnel *t;
|
||||
uint32_t gtp_ifidx;
|
||||
struct in_addr ms, sgsn;
|
||||
uint32_t gtp_version;
|
||||
int optidx;
|
||||
|
||||
|
@ -95,17 +115,8 @@ add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
|
|||
gtp_tunnel_set_o_tei(t, atoi(argv[optidx++]));
|
||||
}
|
||||
|
||||
if (inet_aton(argv[optidx++], &ms) < 0) {
|
||||
perror("bad address for ms");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
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);
|
||||
set_addr(argv[optidx++], true, t);
|
||||
set_addr(argv[optidx++], false, t);
|
||||
|
||||
gtp_add_tunnel(genl_id, nl, t);
|
||||
|
||||
|
|
Loading…
Reference in New Issue