#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0 """Tests for the gtp netlink family.""" import errno from lib.py import ksft_run, ksft_exit from lib.py import ksft_eq, ksft_in, ksft_not_in, ksft_raises from lib.py import KsftSkipEx from lib.py import NetNS, NetNSEnter from lib.py import GtpFamily, NlError from lib.py import CmdExitFailure from lib.py import ip, defer _NO_GTP_ERRORS = ("Unknown device type", "Operation not supported", "Garbage instead of arguments") def _setup(ns): """Return a gtp family handle, and the ifindex of a new gtp device. Create the device first: that loads gtp.ko through its rtnl link alias. ynl resolves the family ids once per process, with a CTRL_CMD_GETFAMILY dump which does not autoload modules, so the family must be registered before the first family handle is created. """ try: ip("link add gtp0 type gtp role ggsn", ns=str(ns)) except CmdExitFailure as e: # gtp.ko not available, or iproute2 without gtp support: any other # failure to create the device is a real error. if not any(msg in e.cmd.stderr for msg in _NO_GTP_ERRORS): raise raise KsftSkipEx("no gtp support (CONFIG_GTP, iproute2)") from e defer(ip, "link del gtp0", ns=str(ns)) ip("link set gtp0 up", ns=str(ns)) link = ip("-d link show gtp0", ns=str(ns), json=True)[0]["ifindex"] with NetNSEnter(str(ns)): gtp = GtpFamily() defer(gtp.close) return gtp, link def _contexts(gtp, link): return [c for c in gtp.getpdp({}, dump=True) if c.get("link") == link] def _iteis(contexts): return [c.get("i-tei") for c in contexts] def add_del_pdp(ns) -> None: """Add a PDP context, query it, then delete it by TEI. The dump must return the context with the link, TEIDs and addresses it was created with, and it must be gone after the delete. The delete only identifies the context by its TEI, to use the GTPv1 TEI lookup. """ gtp, link = _setup(ns) gtp.newpdp({"link": link, "version": 1, "i-tei": 0x100, "o-tei": 0x200, "ms-address": "10.0.0.1", "peer-address": "192.0.2.1"}) pdp = [c for c in _contexts(gtp, link) if c.get("i-tei") == 0x100] ksft_eq(len(pdp), 1) ksft_eq(pdp[0]["o-tei"], 0x200) ksft_eq(pdp[0]["ms-address"], "10.0.0.1") ksft_eq(pdp[0]["peer-address"], "192.0.2.1") gtp.delpdp({"link": link, "version": 1, "i-tei": 0x100}) ksft_not_in(0x100, _iteis(_contexts(gtp, link))) def add_two_pdp(ns) -> None: """Add two PDP contexts, check both are dumped, delete one by address. The delete identifies the context by its MS address, and must leave the other context in place. """ gtp, link = _setup(ns) for i_tei, o_tei, ms in ((0x111, 0x211, "10.0.1.1"), (0x112, 0x212, "10.0.1.2")): gtp.newpdp({"link": link, "version": 1, "i-tei": i_tei, "o-tei": o_tei, "ms-address": ms, "peer-address": "192.0.2.5"}) iteis = _iteis(_contexts(gtp, link)) ksft_in(0x111, iteis) ksft_in(0x112, iteis) gtp.delpdp({"link": link, "version": 1, "ms-address": "10.0.1.1"}) iteis = _iteis(_contexts(gtp, link)) ksft_not_in(0x111, iteis) ksft_in(0x112, iteis) def del_unknown_pdp(ns) -> None: """Deleting a context that was never added must fail with ENOENT. Both lookups are checked: by TEI, and by MS address. """ gtp, link = _setup(ns) for attrs in ({"i-tei": 0x999}, {"ms-address": "10.9.9.9"}): with ksft_raises(NlError) as cm: gtp.delpdp({"link": link, "version": 1, **attrs}) ksft_eq(cm.exception.error, errno.ENOENT) def main() -> None: """Run the PDP context tests in a temporary netns.""" with NetNS() as ns: ksft_run([add_del_pdp, add_two_pdp, del_unknown_pdp], args=(ns,)) ksft_exit() if __name__ == "__main__": main()