diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/gtp-genl.c | 149 | ||||
-rw-r--r-- | src/gtp.c | 42 | ||||
-rw-r--r-- | src/internal.h | 26 | ||||
-rw-r--r-- | src/libgtpnl.map | 9 |
5 files changed, 187 insertions, 41 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index a87c5cc..4d6910e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ include $(top_srcdir)/Make_global.am # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification -LIBVERSION=1:1:1 +LIBVERSION=1:2:1 lib_LTLIBRARIES = libgtpnl.la diff --git a/src/gtp-genl.c b/src/gtp-genl.c index f12f872..33a78d8 100644 --- a/src/gtp-genl.c +++ b/src/gtp-genl.c @@ -44,20 +44,55 @@ static void gtp_build_payload(struct nlmsghdr *nlh, struct gtp_tunnel *t) { - mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version); - if (t->ifns >= 0) + if (t->flags & GTP_TUN_FAMILY) + mnl_attr_put_u8(nlh, GTPA_FAMILY, t->ms_addr.family); + if (t->flags & GTP_TUN_VERSION) + mnl_attr_put_u32(nlh, GTPA_VERSION, t->gtp_version); + if (t->flags & GTP_TUN_IFNS) mnl_attr_put_u32(nlh, GTPA_NET_NS_FD, t->ifns); - mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx); - if (t->sgsn_addr.s_addr) - mnl_attr_put_u32(nlh, GTPA_PEER_ADDRESS, t->sgsn_addr.s_addr); - if (t->ms_addr.s_addr) - mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, t->ms_addr.s_addr); - 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); - } else if (t->gtp_version == GTP_V1) { - mnl_attr_put_u32(nlh, GTPA_I_TEI, t->u.v1.i_tei); - mnl_attr_put_u32(nlh, GTPA_O_TEI, t->u.v1.o_tei); + if (t->flags & GTP_TUN_IFIDX) + mnl_attr_put_u32(nlh, GTPA_LINK, t->ifidx); + + if (t->flags & GTP_TUN_MS_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; + } + } + + if (t->flags & GTP_TUN_SGSN_ADDR) { + 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->flags & GTP_TUN_VERSION) { + if (t->gtp_version == GTP_V0) { + if (t->flags & GTP_TUN_V0_TID) + mnl_attr_put_u64(nlh, GTPA_TID, t->u.v0.tid); + if (t->flags & GTP_TUN_V0_FLOWID) + mnl_attr_put_u16(nlh, GTPA_FLOW, t->u.v0.flowid); + } else if (t->gtp_version == GTP_V1) { + if (t->flags & GTP_TUN_V1_I_TEI) + mnl_attr_put_u32(nlh, GTPA_I_TEI, t->u.v1.i_tei); + if (t->flags & GTP_TUN_V1_O_TEI) + mnl_attr_put_u32(nlh, GTPA_O_TEI, t->u.v1.o_tei); + } } } @@ -77,8 +112,10 @@ int gtp_add_tunnel(int genl_id, struct mnl_socket *nl, struct gtp_tunnel *t) GTP_CMD_NEWPDP); gtp_build_payload(nlh, t); - if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0) + if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0) { perror("genl_socket_talk"); + return -1; + } return 0; } @@ -94,14 +131,17 @@ int gtp_del_tunnel(int genl_id, struct mnl_socket *nl, struct gtp_tunnel *t) GTP_CMD_DELPDP); gtp_build_payload(nlh, t); - if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0) + if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0) { perror("genl_socket_talk"); + return -1; + } return 0; } EXPORT_SYMBOL(gtp_del_tunnel); struct gtp_pdp { + uint32_t ifidx; uint32_t version; union { struct { @@ -112,8 +152,8 @@ struct gtp_pdp { uint32_t o_tei; } v1; } u; - struct in_addr sgsn_addr; - struct in_addr ms_addr; + struct gtp_addr sgsn_addr; + struct gtp_addr ms_addr; }; static int genl_gtp_validate_cb(const struct nlattr *attr, void *data) @@ -125,6 +165,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"); @@ -136,11 +182,20 @@ static int genl_gtp_validate_cb(const struct nlattr *attr, void *data) case GTPA_PEER_ADDRESS: case GTPA_MS_ADDRESS: case GTPA_VERSION: + case GTPA_LINK: if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) { perror("mnl_attr_validate"); 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; } @@ -151,39 +206,65 @@ 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] = {}; - char buf[INET_ADDRSTRLEN]; + char buf[INET6_ADDRSTRLEN]; struct gtp_pdp pdp = {}; struct genlmsghdr *genl; mnl_attr_parse(nlh, sizeof(*genl), genl_gtp_validate_cb, tb); + + if (tb[GTPA_LINK]) + pdp.ifidx = mnl_attr_get_u32(tb[GTPA_LINK]); 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.s_addr = - mnl_attr_get_u32(tb[GTPA_PEER_ADDRESS]); + pdp.sgsn_addr.family = AF_INET; + 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.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]); + pdp.ms_addr.family = AF_INET; + pdp.ms_addr.ip4.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]); + } 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; } - if (tb[GTPA_VERSION]) { - pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]); + + 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; } + printf("%s ", if_indextoname(pdp.ifidx, buf)); + + if (tb[GTPA_VERSION]) + pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]); + printf("version %u ", pdp.version); - if (pdp.version == GTP_V0) { - inet_ntop(AF_INET, &pdp.ms_addr, 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, buf, sizeof(buf)); - printf("tei %u/%u ms_addr %s ", pdp.u.v1.i_tei, - pdp.u.v1.o_tei, buf); - } - inet_ntop(AF_INET, &pdp.sgsn_addr, buf, sizeof(buf)); + + 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; @@ -200,7 +281,7 @@ int gtp_list_tunnel(int genl_id, struct mnl_socket *nl) if (genl_socket_talk(nl, nlh, seq, genl_gtp_attr_cb, NULL) < 0) { perror("genl_socket_talk"); - return 0; + return -1; } return 0; @@ -49,54 +49,88 @@ EXPORT_SYMBOL(gtp_tunnel_free); void gtp_tunnel_set_ifns(struct gtp_tunnel *t, int ifns) { t->ifns = ifns; + t->flags |= GTP_TUN_IFNS; } EXPORT_SYMBOL(gtp_tunnel_set_ifns); void gtp_tunnel_set_ifidx(struct gtp_tunnel *t, uint32_t ifidx) { t->ifidx = ifidx; + t->flags |= GTP_TUN_IFIDX; } EXPORT_SYMBOL(gtp_tunnel_set_ifidx); +void gtp_tunnel_set_family(struct gtp_tunnel *t, uint16_t family) +{ + t->ms_addr.family = family; + t->flags |= GTP_TUN_FAMILY; +} +EXPORT_SYMBOL(gtp_tunnel_set_family); + void gtp_tunnel_set_ms_ip4(struct gtp_tunnel *t, struct in_addr *ms_addr) { - t->ms_addr = *ms_addr; + t->ms_addr.family = AF_INET; + t->ms_addr.ip4 = *ms_addr; + t->flags |= GTP_TUN_FAMILY | GTP_TUN_MS_ADDR; } EXPORT_SYMBOL(gtp_tunnel_set_ms_ip4); void gtp_tunnel_set_sgsn_ip4(struct gtp_tunnel *t, struct in_addr *sgsn_addr) { - t->sgsn_addr = *sgsn_addr; + t->sgsn_addr.family = AF_INET; + t->sgsn_addr.ip4 = *sgsn_addr; + t->flags |= GTP_TUN_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; + t->flags |= GTP_TUN_FAMILY | GTP_TUN_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; + t->flags |= GTP_TUN_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; + t->flags |= GTP_TUN_VERSION; } EXPORT_SYMBOL(gtp_tunnel_set_version); void gtp_tunnel_set_tid(struct gtp_tunnel *t, uint64_t tid) { t->u.v0.tid = tid; + t->flags |= GTP_TUN_V0_TID; } EXPORT_SYMBOL(gtp_tunnel_set_tid); void gtp_tunnel_set_flowid(struct gtp_tunnel *t, uint16_t flowid) { t->u.v0.flowid = flowid; + t->flags |= GTP_TUN_V0_FLOWID; } EXPORT_SYMBOL(gtp_tunnel_set_flowid); void gtp_tunnel_set_i_tei(struct gtp_tunnel *t, uint32_t i_tei) { t->u.v1.i_tei = i_tei; + t->flags |= GTP_TUN_V1_I_TEI; } EXPORT_SYMBOL(gtp_tunnel_set_i_tei); void gtp_tunnel_set_o_tei(struct gtp_tunnel *t, uint32_t o_tei) { t->u.v1.o_tei = o_tei; + t->flags |= GTP_TUN_V1_O_TEI; } EXPORT_SYMBOL(gtp_tunnel_set_o_tei); @@ -114,13 +148,13 @@ EXPORT_SYMBOL(gtp_tunnel_get_ifidx); const struct in_addr *gtp_tunnel_get_ms_ip4(struct gtp_tunnel *t) { - return &t->ms_addr; + return &t->ms_addr.ip4; } EXPORT_SYMBOL(gtp_tunnel_get_ms_ip4); const struct in_addr *gtp_tunnel_get_sgsn_ip4(struct gtp_tunnel *t) { - return &t->sgsn_addr; + return &t->sgsn_addr.ip4; } EXPORT_SYMBOL(gtp_tunnel_get_sgsn_ip4); diff --git a/src/internal.h b/src/internal.h index 1754e3b..1433621 100644 --- a/src/internal.h +++ b/src/internal.h @@ -12,11 +12,32 @@ #include <stdint.h> #include <netinet/in.h> +struct gtp_addr { + sa_family_t family; + union { + struct in_addr ip4; + struct in6_addr ip6; + }; +}; + +enum { + GTP_TUN_IFNS = (1 << 0), + GTP_TUN_IFIDX = (1 << 1), + GTP_TUN_FAMILY = (1 << 2), + GTP_TUN_MS_ADDR = (1 << 3), + GTP_TUN_SGSN_ADDR = (1 << 4), + GTP_TUN_VERSION = (1 << 5), + GTP_TUN_V0_TID = (1 << 6), + GTP_TUN_V0_FLOWID = (1 << 7), + GTP_TUN_V1_I_TEI = (1 << 8), + GTP_TUN_V1_O_TEI = (1 << 9), +}; + struct gtp_tunnel { int ifns; uint32_t ifidx; - struct in_addr ms_addr; - struct in_addr sgsn_addr; + struct gtp_addr ms_addr; + struct gtp_addr sgsn_addr; int gtp_version; union { struct { @@ -28,6 +49,7 @@ struct gtp_tunnel { uint32_t o_tei; } v1; } u; + uint32_t flags; }; #endif diff --git a/src/libgtpnl.map b/src/libgtpnl.map index 804f8b3..41b7dd7 100644 --- a/src/libgtpnl.map +++ b/src/libgtpnl.map @@ -38,3 +38,12 @@ global: local: *; }; + +LIBGTPNL_1.1 { + gtp_tunnel_set_ms_ip6; + gtp_tunnel_set_sgsn_ip6; +} LIBGTPNL_1.0; + +LIBGTPNL_1.2 { + gtp_tunnel_set_family; +} LIBGTPNL_1.1; |