1 // SPDX-License-Identifier: GPL-2.0 1 2 /* Copyright (c) 2023 Isovalent */ 3 4 #include <linux/bpf.h> 5 #include <linux/bpf_mprog.h> 6 #include <linux/netdevice.h> 7 8 #include <net/tcx.h> 9 10 int tcx_prog_attach(const union bpf_attr *attr 11 { 12 bool created, ingress = attr->attach_t 13 struct net *net = current->nsproxy->ne 14 struct bpf_mprog_entry *entry, *entry_ 15 struct bpf_prog *replace_prog = NULL; 16 struct net_device *dev; 17 int ret; 18 19 rtnl_lock(); 20 dev = __dev_get_by_index(net, attr->ta 21 if (!dev) { 22 ret = -ENODEV; 23 goto out; 24 } 25 if (attr->attach_flags & BPF_F_REPLACE 26 replace_prog = bpf_prog_get_ty 27 28 if (IS_ERR(replace_prog)) { 29 ret = PTR_ERR(replace_ 30 replace_prog = NULL; 31 goto out; 32 } 33 } 34 entry = tcx_entry_fetch_or_create(dev, 35 if (!entry) { 36 ret = -ENOMEM; 37 goto out; 38 } 39 ret = bpf_mprog_attach(entry, &entry_n 40 attr->attach_fl 41 attr->expected_ 42 if (!ret) { 43 if (entry != entry_new) { 44 tcx_entry_update(dev, 45 tcx_entry_sync(); 46 tcx_skeys_inc(ingress) 47 } 48 bpf_mprog_commit(entry); 49 } else if (created) { 50 tcx_entry_free(entry); 51 } 52 out: 53 if (replace_prog) 54 bpf_prog_put(replace_prog); 55 rtnl_unlock(); 56 return ret; 57 } 58 59 int tcx_prog_detach(const union bpf_attr *attr 60 { 61 bool ingress = attr->attach_type == BP 62 struct net *net = current->nsproxy->ne 63 struct bpf_mprog_entry *entry, *entry_ 64 struct net_device *dev; 65 int ret; 66 67 rtnl_lock(); 68 dev = __dev_get_by_index(net, attr->ta 69 if (!dev) { 70 ret = -ENODEV; 71 goto out; 72 } 73 entry = tcx_entry_fetch(dev, ingress); 74 if (!entry) { 75 ret = -ENOENT; 76 goto out; 77 } 78 ret = bpf_mprog_detach(entry, &entry_n 79 attr->relative_ 80 if (!ret) { 81 if (!tcx_entry_is_active(entry 82 entry_new = NULL; 83 tcx_entry_update(dev, entry_ne 84 tcx_entry_sync(); 85 tcx_skeys_dec(ingress); 86 bpf_mprog_commit(entry); 87 if (!entry_new) 88 tcx_entry_free(entry); 89 } 90 out: 91 rtnl_unlock(); 92 return ret; 93 } 94 95 void tcx_uninstall(struct net_device *dev, boo 96 { 97 struct bpf_mprog_entry *entry, *entry_ 98 struct bpf_tuple tuple = {}; 99 struct bpf_mprog_fp *fp; 100 struct bpf_mprog_cp *cp; 101 bool active; 102 103 entry = tcx_entry_fetch(dev, ingress); 104 if (!entry) 105 return; 106 active = tcx_entry(entry)->miniq_activ 107 if (active) 108 bpf_mprog_clear_all(entry, &en 109 tcx_entry_update(dev, entry_new, ingre 110 tcx_entry_sync(); 111 bpf_mprog_foreach_tuple(entry, fp, cp, 112 if (tuple.link) 113 tcx_link(tuple.link)-> 114 else 115 bpf_prog_put(tuple.pro 116 tcx_skeys_dec(ingress); 117 } 118 if (!active) 119 tcx_entry_free(entry); 120 } 121 122 int tcx_prog_query(const union bpf_attr *attr, 123 { 124 bool ingress = attr->query.attach_type 125 struct net *net = current->nsproxy->ne 126 struct net_device *dev; 127 int ret; 128 129 rtnl_lock(); 130 dev = __dev_get_by_index(net, attr->qu 131 if (!dev) { 132 ret = -ENODEV; 133 goto out; 134 } 135 ret = bpf_mprog_query(attr, uattr, tcx 136 out: 137 rtnl_unlock(); 138 return ret; 139 } 140 141 static int tcx_link_prog_attach(struct bpf_lin 142 u64 revision) 143 { 144 struct tcx_link *tcx = tcx_link(link); 145 bool created, ingress = tcx->location 146 struct bpf_mprog_entry *entry, *entry_ 147 struct net_device *dev = tcx->dev; 148 int ret; 149 150 ASSERT_RTNL(); 151 entry = tcx_entry_fetch_or_create(dev, 152 if (!entry) 153 return -ENOMEM; 154 ret = bpf_mprog_attach(entry, &entry_n 155 id_or_fd, revis 156 if (!ret) { 157 if (entry != entry_new) { 158 tcx_entry_update(dev, 159 tcx_entry_sync(); 160 tcx_skeys_inc(ingress) 161 } 162 bpf_mprog_commit(entry); 163 } else if (created) { 164 tcx_entry_free(entry); 165 } 166 return ret; 167 } 168 169 static void tcx_link_release(struct bpf_link * 170 { 171 struct tcx_link *tcx = tcx_link(link); 172 bool ingress = tcx->location == BPF_TC 173 struct bpf_mprog_entry *entry, *entry_ 174 struct net_device *dev; 175 int ret = 0; 176 177 rtnl_lock(); 178 dev = tcx->dev; 179 if (!dev) 180 goto out; 181 entry = tcx_entry_fetch(dev, ingress); 182 if (!entry) { 183 ret = -ENOENT; 184 goto out; 185 } 186 ret = bpf_mprog_detach(entry, &entry_n 187 if (!ret) { 188 if (!tcx_entry_is_active(entry 189 entry_new = NULL; 190 tcx_entry_update(dev, entry_ne 191 tcx_entry_sync(); 192 tcx_skeys_dec(ingress); 193 bpf_mprog_commit(entry); 194 if (!entry_new) 195 tcx_entry_free(entry); 196 tcx->dev = NULL; 197 } 198 out: 199 WARN_ON_ONCE(ret); 200 rtnl_unlock(); 201 } 202 203 static int tcx_link_update(struct bpf_link *li 204 struct bpf_prog *op 205 { 206 struct tcx_link *tcx = tcx_link(link); 207 bool ingress = tcx->location == BPF_TC 208 struct bpf_mprog_entry *entry, *entry_ 209 struct net_device *dev; 210 int ret = 0; 211 212 rtnl_lock(); 213 dev = tcx->dev; 214 if (!dev) { 215 ret = -ENOLINK; 216 goto out; 217 } 218 if (oprog && link->prog != oprog) { 219 ret = -EPERM; 220 goto out; 221 } 222 oprog = link->prog; 223 if (oprog == nprog) { 224 bpf_prog_put(nprog); 225 goto out; 226 } 227 entry = tcx_entry_fetch(dev, ingress); 228 if (!entry) { 229 ret = -ENOENT; 230 goto out; 231 } 232 ret = bpf_mprog_attach(entry, &entry_n 233 BPF_F_REPLACE | 234 link->prog->aux 235 if (!ret) { 236 WARN_ON_ONCE(entry != entry_ne 237 oprog = xchg(&link->prog, npro 238 bpf_prog_put(oprog); 239 bpf_mprog_commit(entry); 240 } 241 out: 242 rtnl_unlock(); 243 return ret; 244 } 245 246 static void tcx_link_dealloc(struct bpf_link * 247 { 248 kfree(tcx_link(link)); 249 } 250 251 static void tcx_link_fdinfo(const struct bpf_l 252 { 253 const struct tcx_link *tcx = tcx_link( 254 u32 ifindex = 0; 255 256 rtnl_lock(); 257 if (tcx->dev) 258 ifindex = tcx->dev->ifindex; 259 rtnl_unlock(); 260 261 seq_printf(seq, "ifindex:\t%u\n", ifin 262 seq_printf(seq, "attach_type:\t%u (%s) 263 tcx->location, 264 tcx->location == BPF_TCX_IN 265 } 266 267 static int tcx_link_fill_info(const struct bpf 268 struct bpf_link_ 269 { 270 const struct tcx_link *tcx = tcx_link( 271 u32 ifindex = 0; 272 273 rtnl_lock(); 274 if (tcx->dev) 275 ifindex = tcx->dev->ifindex; 276 rtnl_unlock(); 277 278 info->tcx.ifindex = ifindex; 279 info->tcx.attach_type = tcx->location; 280 return 0; 281 } 282 283 static int tcx_link_detach(struct bpf_link *li 284 { 285 tcx_link_release(link); 286 return 0; 287 } 288 289 static const struct bpf_link_ops tcx_link_lops 290 .release = tcx_link_release, 291 .detach = tcx_link_detach, 292 .dealloc = tcx_link_dealloc, 293 .update_prog = tcx_link_update, 294 .show_fdinfo = tcx_link_fdinfo, 295 .fill_link_info = tcx_link_fill_info, 296 }; 297 298 static int tcx_link_init(struct tcx_link *tcx, 299 struct bpf_link_prime 300 const union bpf_attr 301 struct net_device *de 302 struct bpf_prog *prog 303 { 304 bpf_link_init(&tcx->link, BPF_LINK_TYP 305 tcx->location = attr->link_create.atta 306 tcx->dev = dev; 307 return bpf_link_prime(&tcx->link, link 308 } 309 310 int tcx_link_attach(const union bpf_attr *attr 311 { 312 struct net *net = current->nsproxy->ne 313 struct bpf_link_primer link_primer; 314 struct net_device *dev; 315 struct tcx_link *tcx; 316 int ret; 317 318 rtnl_lock(); 319 dev = __dev_get_by_index(net, attr->li 320 if (!dev) { 321 ret = -ENODEV; 322 goto out; 323 } 324 tcx = kzalloc(sizeof(*tcx), GFP_USER); 325 if (!tcx) { 326 ret = -ENOMEM; 327 goto out; 328 } 329 ret = tcx_link_init(tcx, &link_primer, 330 if (ret) { 331 kfree(tcx); 332 goto out; 333 } 334 ret = tcx_link_prog_attach(&tcx->link, 335 attr->link_ 336 attr->link_ 337 if (ret) { 338 tcx->dev = NULL; 339 bpf_link_cleanup(&link_primer) 340 goto out; 341 } 342 ret = bpf_link_settle(&link_primer); 343 out: 344 rtnl_unlock(); 345 return ret; 346 } 347
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.