| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* GTP according to GSM TS 09.60 / 3GPP TS 29.060 |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * (C) 2012-2014 by sysmocom - s.f.m.c. GmbH |
|---|
| .. | .. |
|---|
| 6 | 7 | * Author: Harald Welte <hwelte@sysmocom.de> |
|---|
| 7 | 8 | * Pablo Neira Ayuso <pablo@netfilter.org> |
|---|
| 8 | 9 | * Andreas Schultz <aschultz@travelping.com> |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is free software; you can redistribute it and/or |
|---|
| 11 | | - * modify it under the terms of the GNU General Public License |
|---|
| 12 | | - * as published by the Free Software Foundation; either version |
|---|
| 13 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 14 | 10 | */ |
|---|
| 15 | 11 | |
|---|
| 16 | 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| .. | .. |
|---|
| 186 | 182 | static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb, |
|---|
| 187 | 183 | unsigned int hdrlen, unsigned int role) |
|---|
| 188 | 184 | { |
|---|
| 189 | | - struct pcpu_sw_netstats *stats; |
|---|
| 190 | | - |
|---|
| 191 | 185 | if (!gtp_check_ms(skb, pctx, hdrlen, role)) { |
|---|
| 192 | 186 | netdev_dbg(pctx->dev, "No PDP ctx for this MS\n"); |
|---|
| 193 | 187 | return 1; |
|---|
| .. | .. |
|---|
| 208 | 202 | |
|---|
| 209 | 203 | skb->dev = pctx->dev; |
|---|
| 210 | 204 | |
|---|
| 211 | | - stats = this_cpu_ptr(pctx->dev->tstats); |
|---|
| 212 | | - u64_stats_update_begin(&stats->syncp); |
|---|
| 213 | | - stats->rx_packets++; |
|---|
| 214 | | - stats->rx_bytes += skb->len; |
|---|
| 215 | | - u64_stats_update_end(&stats->syncp); |
|---|
| 205 | + dev_sw_netstats_rx_add(pctx->dev, skb->len); |
|---|
| 216 | 206 | |
|---|
| 217 | 207 | netif_rx(skb); |
|---|
| 218 | 208 | return 0; |
|---|
| .. | .. |
|---|
| 714 | 704 | hlist_for_each_entry_rcu(pctx, >p->tid_hash[i], hlist_tid) |
|---|
| 715 | 705 | pdp_context_delete(pctx); |
|---|
| 716 | 706 | |
|---|
| 717 | | - gtp_encap_disable(gtp); |
|---|
| 718 | 707 | list_del_rcu(>p->list); |
|---|
| 719 | 708 | unregister_netdevice_queue(dev, head); |
|---|
| 720 | 709 | } |
|---|
| .. | .. |
|---|
| 858 | 847 | |
|---|
| 859 | 848 | sk1u = gtp_encap_enable_socket(fd1, UDP_ENCAP_GTP1U, gtp); |
|---|
| 860 | 849 | if (IS_ERR(sk1u)) { |
|---|
| 861 | | - if (sk0) |
|---|
| 862 | | - gtp_encap_disable_sock(sk0); |
|---|
| 850 | + gtp_encap_disable_sock(sk0); |
|---|
| 863 | 851 | return PTR_ERR(sk1u); |
|---|
| 864 | 852 | } |
|---|
| 865 | 853 | } |
|---|
| .. | .. |
|---|
| 867 | 855 | if (data[IFLA_GTP_ROLE]) { |
|---|
| 868 | 856 | role = nla_get_u32(data[IFLA_GTP_ROLE]); |
|---|
| 869 | 857 | if (role > GTP_ROLE_SGSN) { |
|---|
| 870 | | - if (sk0) |
|---|
| 871 | | - gtp_encap_disable_sock(sk0); |
|---|
| 872 | | - if (sk1u) |
|---|
| 873 | | - gtp_encap_disable_sock(sk1u); |
|---|
| 858 | + gtp_encap_disable_sock(sk0); |
|---|
| 859 | + gtp_encap_disable_sock(sk1u); |
|---|
| 874 | 860 | return -EINVAL; |
|---|
| 875 | 861 | } |
|---|
| 876 | 862 | } |
|---|
| .. | .. |
|---|
| 935 | 921 | } |
|---|
| 936 | 922 | } |
|---|
| 937 | 923 | |
|---|
| 938 | | -static int gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk, |
|---|
| 939 | | - struct genl_info *info) |
|---|
| 924 | +static struct pdp_ctx *gtp_pdp_add(struct gtp_dev *gtp, struct sock *sk, |
|---|
| 925 | + struct genl_info *info) |
|---|
| 940 | 926 | { |
|---|
| 941 | 927 | struct pdp_ctx *pctx, *pctx_tid = NULL; |
|---|
| 942 | 928 | struct net_device *dev = gtp->dev; |
|---|
| .. | .. |
|---|
| 963 | 949 | |
|---|
| 964 | 950 | if (found) { |
|---|
| 965 | 951 | if (info->nlhdr->nlmsg_flags & NLM_F_EXCL) |
|---|
| 966 | | - return -EEXIST; |
|---|
| 952 | + return ERR_PTR(-EEXIST); |
|---|
| 967 | 953 | if (info->nlhdr->nlmsg_flags & NLM_F_REPLACE) |
|---|
| 968 | | - return -EOPNOTSUPP; |
|---|
| 954 | + return ERR_PTR(-EOPNOTSUPP); |
|---|
| 969 | 955 | |
|---|
| 970 | 956 | if (pctx && pctx_tid) |
|---|
| 971 | | - return -EEXIST; |
|---|
| 957 | + return ERR_PTR(-EEXIST); |
|---|
| 972 | 958 | if (!pctx) |
|---|
| 973 | 959 | pctx = pctx_tid; |
|---|
| 974 | 960 | |
|---|
| .. | .. |
|---|
| 981 | 967 | netdev_dbg(dev, "GTPv1-U: update tunnel id = %x/%x (pdp %p)\n", |
|---|
| 982 | 968 | pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); |
|---|
| 983 | 969 | |
|---|
| 984 | | - return 0; |
|---|
| 970 | + return pctx; |
|---|
| 985 | 971 | |
|---|
| 986 | 972 | } |
|---|
| 987 | 973 | |
|---|
| 988 | 974 | pctx = kmalloc(sizeof(*pctx), GFP_ATOMIC); |
|---|
| 989 | 975 | if (pctx == NULL) |
|---|
| 990 | | - return -ENOMEM; |
|---|
| 976 | + return ERR_PTR(-ENOMEM); |
|---|
| 991 | 977 | |
|---|
| 992 | 978 | sock_hold(sk); |
|---|
| 993 | 979 | pctx->sk = sk; |
|---|
| .. | .. |
|---|
| 1025 | 1011 | break; |
|---|
| 1026 | 1012 | } |
|---|
| 1027 | 1013 | |
|---|
| 1028 | | - return 0; |
|---|
| 1014 | + return pctx; |
|---|
| 1029 | 1015 | } |
|---|
| 1030 | 1016 | |
|---|
| 1031 | 1017 | static void pdp_context_free(struct rcu_head *head) |
|---|
| .. | .. |
|---|
| 1043 | 1029 | call_rcu(&pctx->rcu_head, pdp_context_free); |
|---|
| 1044 | 1030 | } |
|---|
| 1045 | 1031 | |
|---|
| 1032 | +static int gtp_tunnel_notify(struct pdp_ctx *pctx, u8 cmd, gfp_t allocation); |
|---|
| 1033 | + |
|---|
| 1046 | 1034 | static int gtp_genl_new_pdp(struct sk_buff *skb, struct genl_info *info) |
|---|
| 1047 | 1035 | { |
|---|
| 1048 | 1036 | unsigned int version; |
|---|
| 1037 | + struct pdp_ctx *pctx; |
|---|
| 1049 | 1038 | struct gtp_dev *gtp; |
|---|
| 1050 | 1039 | struct sock *sk; |
|---|
| 1051 | 1040 | int err; |
|---|
| .. | .. |
|---|
| 1075 | 1064 | } |
|---|
| 1076 | 1065 | |
|---|
| 1077 | 1066 | rtnl_lock(); |
|---|
| 1078 | | - rcu_read_lock(); |
|---|
| 1079 | 1067 | |
|---|
| 1080 | 1068 | gtp = gtp_find_dev(sock_net(skb->sk), info->attrs); |
|---|
| 1081 | 1069 | if (!gtp) { |
|---|
| .. | .. |
|---|
| 1095 | 1083 | goto out_unlock; |
|---|
| 1096 | 1084 | } |
|---|
| 1097 | 1085 | |
|---|
| 1098 | | - err = gtp_pdp_add(gtp, sk, info); |
|---|
| 1086 | + pctx = gtp_pdp_add(gtp, sk, info); |
|---|
| 1087 | + if (IS_ERR(pctx)) { |
|---|
| 1088 | + err = PTR_ERR(pctx); |
|---|
| 1089 | + } else { |
|---|
| 1090 | + gtp_tunnel_notify(pctx, GTP_CMD_NEWPDP, GFP_KERNEL); |
|---|
| 1091 | + err = 0; |
|---|
| 1092 | + } |
|---|
| 1099 | 1093 | |
|---|
| 1100 | 1094 | out_unlock: |
|---|
| 1101 | | - rcu_read_unlock(); |
|---|
| 1102 | 1095 | rtnl_unlock(); |
|---|
| 1103 | 1096 | return err; |
|---|
| 1104 | 1097 | } |
|---|
| .. | .. |
|---|
| 1166 | 1159 | netdev_dbg(pctx->dev, "GTPv1-U: deleting tunnel id = %x/%x (pdp %p)\n", |
|---|
| 1167 | 1160 | pctx->u.v1.i_tei, pctx->u.v1.o_tei, pctx); |
|---|
| 1168 | 1161 | |
|---|
| 1162 | + gtp_tunnel_notify(pctx, GTP_CMD_DELPDP, GFP_ATOMIC); |
|---|
| 1169 | 1163 | pdp_context_delete(pctx); |
|---|
| 1170 | 1164 | |
|---|
| 1171 | 1165 | out_unlock: |
|---|
| .. | .. |
|---|
| 1174 | 1168 | } |
|---|
| 1175 | 1169 | |
|---|
| 1176 | 1170 | static struct genl_family gtp_genl_family; |
|---|
| 1171 | + |
|---|
| 1172 | +enum gtp_multicast_groups { |
|---|
| 1173 | + GTP_GENL_MCGRP, |
|---|
| 1174 | +}; |
|---|
| 1175 | + |
|---|
| 1176 | +static const struct genl_multicast_group gtp_genl_mcgrps[] = { |
|---|
| 1177 | + [GTP_GENL_MCGRP] = { .name = GTP_GENL_MCGRP_NAME }, |
|---|
| 1178 | +}; |
|---|
| 1177 | 1179 | |
|---|
| 1178 | 1180 | static int gtp_genl_fill_info(struct sk_buff *skb, u32 snd_portid, u32 snd_seq, |
|---|
| 1179 | 1181 | int flags, u32 type, struct pdp_ctx *pctx) |
|---|
| .. | .. |
|---|
| 1210 | 1212 | nla_put_failure: |
|---|
| 1211 | 1213 | genlmsg_cancel(skb, genlh); |
|---|
| 1212 | 1214 | return -EMSGSIZE; |
|---|
| 1215 | +} |
|---|
| 1216 | + |
|---|
| 1217 | +static int gtp_tunnel_notify(struct pdp_ctx *pctx, u8 cmd, gfp_t allocation) |
|---|
| 1218 | +{ |
|---|
| 1219 | + struct sk_buff *msg; |
|---|
| 1220 | + int ret; |
|---|
| 1221 | + |
|---|
| 1222 | + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, allocation); |
|---|
| 1223 | + if (!msg) |
|---|
| 1224 | + return -ENOMEM; |
|---|
| 1225 | + |
|---|
| 1226 | + ret = gtp_genl_fill_info(msg, 0, 0, 0, cmd, pctx); |
|---|
| 1227 | + if (ret < 0) { |
|---|
| 1228 | + nlmsg_free(msg); |
|---|
| 1229 | + return ret; |
|---|
| 1230 | + } |
|---|
| 1231 | + |
|---|
| 1232 | + ret = genlmsg_multicast_netns(>p_genl_family, dev_net(pctx->dev), msg, |
|---|
| 1233 | + 0, GTP_GENL_MCGRP, GFP_ATOMIC); |
|---|
| 1234 | + return ret; |
|---|
| 1213 | 1235 | } |
|---|
| 1214 | 1236 | |
|---|
| 1215 | 1237 | static int gtp_genl_get_pdp(struct sk_buff *skb, struct genl_info *info) |
|---|
| .. | .. |
|---|
| 1310 | 1332 | [GTPA_O_TEI] = { .type = NLA_U32, }, |
|---|
| 1311 | 1333 | }; |
|---|
| 1312 | 1334 | |
|---|
| 1313 | | -static const struct genl_ops gtp_genl_ops[] = { |
|---|
| 1335 | +static const struct genl_small_ops gtp_genl_ops[] = { |
|---|
| 1314 | 1336 | { |
|---|
| 1315 | 1337 | .cmd = GTP_CMD_NEWPDP, |
|---|
| 1338 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 1316 | 1339 | .doit = gtp_genl_new_pdp, |
|---|
| 1317 | | - .policy = gtp_genl_policy, |
|---|
| 1318 | 1340 | .flags = GENL_ADMIN_PERM, |
|---|
| 1319 | 1341 | }, |
|---|
| 1320 | 1342 | { |
|---|
| 1321 | 1343 | .cmd = GTP_CMD_DELPDP, |
|---|
| 1344 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 1322 | 1345 | .doit = gtp_genl_del_pdp, |
|---|
| 1323 | | - .policy = gtp_genl_policy, |
|---|
| 1324 | 1346 | .flags = GENL_ADMIN_PERM, |
|---|
| 1325 | 1347 | }, |
|---|
| 1326 | 1348 | { |
|---|
| 1327 | 1349 | .cmd = GTP_CMD_GETPDP, |
|---|
| 1350 | + .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
|---|
| 1328 | 1351 | .doit = gtp_genl_get_pdp, |
|---|
| 1329 | 1352 | .dumpit = gtp_genl_dump_pdp, |
|---|
| 1330 | | - .policy = gtp_genl_policy, |
|---|
| 1331 | 1353 | .flags = GENL_ADMIN_PERM, |
|---|
| 1332 | 1354 | }, |
|---|
| 1333 | 1355 | }; |
|---|
| .. | .. |
|---|
| 1337 | 1359 | .version = 0, |
|---|
| 1338 | 1360 | .hdrsize = 0, |
|---|
| 1339 | 1361 | .maxattr = GTPA_MAX, |
|---|
| 1362 | + .policy = gtp_genl_policy, |
|---|
| 1340 | 1363 | .netnsok = true, |
|---|
| 1341 | 1364 | .module = THIS_MODULE, |
|---|
| 1342 | | - .ops = gtp_genl_ops, |
|---|
| 1343 | | - .n_ops = ARRAY_SIZE(gtp_genl_ops), |
|---|
| 1365 | + .small_ops = gtp_genl_ops, |
|---|
| 1366 | + .n_small_ops = ARRAY_SIZE(gtp_genl_ops), |
|---|
| 1367 | + .mcgrps = gtp_genl_mcgrps, |
|---|
| 1368 | + .n_mcgrps = ARRAY_SIZE(gtp_genl_mcgrps), |
|---|
| 1344 | 1369 | }; |
|---|
| 1345 | 1370 | |
|---|
| 1346 | 1371 | static int __net_init gtp_net_init(struct net *net) |
|---|