diff --git a/include/libgtpnl/gtp.h b/include/libgtpnl/gtp.h index a6cd8e2..9b0bcf4 100644 --- a/include/libgtpnl/gtp.h +++ b/include/libgtpnl/gtp.h @@ -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); diff --git a/include/linux/gtp.h b/include/linux/gtp.h index 3dcdb9e..40f5388 100644 --- a/include/linux/gtp.h +++ b/include/linux/gtp.h @@ -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) diff --git a/src/gtp-genl.c b/src/gtp-genl.c index 82ef8ab..c55fbd9 100644 --- a/src/gtp-genl.c +++ b/src/gtp-genl.c @@ -44,14 +44,30 @@ 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; + } + + 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; + } + 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 +145,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 +167,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 +190,70 @@ 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; + } + + 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); + + switch (pdp.ms_addr.family) { + case AF_INET: + inet_ntop(AF_INET, &pdp.ms_addr.ip4, buf, sizeof(buf)); + break; + case AF_INET6: + inet_ntop(AF_INET6, &pdp.ms_addr.ip6, buf, sizeof(buf)); + break; + } + printf("ms_addr %s ", buf); + + switch (pdp.sgsn_addr.family) { + case AF_INET: + inet_ntop(AF_INET, &pdp.sgsn_addr.ip4, buf, sizeof(buf)); + break; + case AF_INET6: + inet_ntop(AF_INET6, &pdp.sgsn_addr.ip6, buf, sizeof(buf)); + break; } - inet_ntop(AF_INET, &pdp.sgsn_addr.ip4, buf, sizeof(buf)); printf("sgsn_addr %s\n", buf); return MNL_CB_OK; diff --git a/src/gtp.c b/src/gtp.c index 4eadc29..657eb7a 100644 --- a/src/gtp.c +++ b/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; diff --git a/src/libgtpnl.map b/src/libgtpnl.map index 804f8b3..1237e69 100644 --- a/src/libgtpnl.map +++ b/src/libgtpnl.map @@ -38,3 +38,8 @@ global: local: *; }; + +LIBGTPNL_1.1 { + gtp_tunnel_set_ms_ip6; + gtp_tunnel_set_sgsn_ip6; +} LIBGTPNL_1.0; diff --git a/tools/gtp-tunnel.c b/tools/gtp-tunnel.c index 1868097..b29e386 100644 --- a/tools/gtp-tunnel.c +++ b/tools/gtp-tunnel.c @@ -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);