Linux lhjmq-records 5.15.0-118-generic #128-Ubuntu SMP Fri Jul 5 09:28:59 UTC 2024 x86_64
Your IP : 3.142.42.247
#include <Python.h>
#include <ctype.h>
#include <errno.h>
#include <net/if.h>
#include <netlink/cache.h>
#include <netlink/route/addr.h>
#include <netlink/route/link.h>
#include <netlink/route/link/vlan.h>
#include <netlink/route/route.h>
#define NL_CB_me NL_CB_DEFAULT
static char *act2str(int act) {
#define C2S(x) \
case x: \
return &#x[7]
switch (act) {
C2S(NL_ACT_UNSPEC);
C2S(NL_ACT_NEW);
C2S(NL_ACT_DEL);
C2S(NL_ACT_GET);
C2S(NL_ACT_SET);
C2S(NL_ACT_CHANGE);
default:
return "???";
}
#undef C2S
}
struct Listener {
PyObject_HEAD
struct nl_cache_mngr *mngr;
struct nl_cache *link_cache;
struct nl_cache *route_cache;
PyObject *observer;
PyObject *exc_typ, *exc_val, *exc_tb;
};
struct _clear_routes_arg {
struct Listener *listener;
int ifindex;
};
static void observe_route_change(
int act,
struct rtnl_route *route,
struct Listener* listener);
static void _clear_routes(struct nl_object *ob, void *data) {
struct _clear_routes_arg* arg = (struct _clear_routes_arg*)data;
struct rtnl_route* route = (struct rtnl_route*)ob;
if (rtnl_route_get_nnexthops(route) > 0) {
// Bit cheaty to ignore multipath but....
struct rtnl_nexthop* nh = rtnl_route_nexthop_n(route, 0);
if (rtnl_route_nh_get_ifindex(nh) == arg->ifindex) {
observe_route_change(NL_ACT_DEL, route, arg->listener);
nl_cache_remove(ob);
}
}
}
static void observe_link_change(
int act,
struct rtnl_link *old_link,
struct rtnl_link *link,
struct Listener* listener)
{
if (listener->exc_typ != NULL || listener->observer == Py_None) {
return;
}
PyObject *data;
struct _clear_routes_arg clear_routes_arg;
int is_vlan, ifindex;
unsigned int flags;
if (act == NL_ACT_DEL) {
link = old_link;
}
is_vlan = rtnl_link_is_vlan(link);
ifindex = rtnl_link_get_ifindex(link);
flags = rtnl_link_get_flags(link);
if (!(flags & IFF_UP)) {
if (old_link && (rtnl_link_get_flags(old_link) & IFF_UP)) {
clear_routes_arg.ifindex = ifindex;
clear_routes_arg.listener = listener;
nl_cache_foreach(listener->route_cache, _clear_routes, &clear_routes_arg);
}
}
data = Py_BuildValue(
"{si sI sI si sN}",
"ifindex", ifindex,
"flags", flags,
"arptype", rtnl_link_get_arptype(link),
"family", rtnl_link_get_family(link),
"is_vlan", PyBool_FromLong(is_vlan));
if (data == NULL) {
goto exit;
}
if (rtnl_link_get_name(link) != NULL) {
PyObject *ob = PyBytes_FromString(rtnl_link_get_name(link));
if (ob == NULL || PyDict_SetItemString(data, "name", ob) < 0) {
Py_XDECREF(ob);
goto exit;
}
Py_DECREF(ob);
}
if (is_vlan) {
PyObject* v;
v = PyLong_FromLong(rtnl_link_vlan_get_id(link));
if (v == NULL || PyDict_SetItemString(data, "vlan_id", v) < 0) {
Py_XDECREF(v);
goto exit;
}
Py_DECREF(v);
v = PyLong_FromLong(rtnl_link_get_link(link));
if (v == NULL || PyDict_SetItemString(data, "vlan_link", v) < 0) {
Py_XDECREF(v);
goto exit;
}
Py_DECREF(v);
}
PyObject *r = PyObject_CallMethod(listener->observer, "link_change", "sO", act2str(act), data);
Py_XDECREF(r);
exit:
Py_XDECREF(data);
if (PyErr_Occurred()) {
PyErr_Fetch(&listener->exc_typ, &listener->exc_val, &listener->exc_tb);
}
}
static void _cb_link(struct nl_cache *cache, struct nl_object *old, struct nl_object *new, uint64_t diff, int act,
void *data) {
observe_link_change(act, (struct rtnl_link *)old, (struct rtnl_link *)new, (struct Listener*)data);
}
static void _e_link(struct nl_object *ob, void *data) {
observe_link_change(NL_ACT_NEW, NULL, (struct rtnl_link *)ob, (struct Listener*)data);
}
static void observe_addr_change(
int act,
struct rtnl_addr *addr,
struct Listener* listener)
{
if (listener->exc_typ != NULL || listener->observer == Py_None) {
return;
}
PyObject *data;
data = Py_BuildValue(
"{si sI si si}",
"ifindex", rtnl_addr_get_ifindex(addr),
"flags", rtnl_addr_get_flags(addr),
"family", rtnl_addr_get_family(addr),
"scope", rtnl_addr_get_scope(addr));
if (data == NULL) {
goto exit;
}
struct nl_addr *local = rtnl_addr_get_local(addr);
if (local != NULL) {
char buf[100];
PyObject *ob = PyBytes_FromString(nl_addr2str(local, buf, 100));
if (ob == NULL || PyDict_SetItemString(data, "local", ob) < 0) {
Py_XDECREF(ob);
goto exit;
}
Py_DECREF(ob);
}
PyObject *r = PyObject_CallMethod(listener->observer, "addr_change", "sO", act2str(act), data);
Py_XDECREF(r);
exit:
Py_XDECREF(data);
if (PyErr_Occurred()) {
PyErr_Fetch(&listener->exc_typ, &listener->exc_val, &listener->exc_tb);
}
}
static void _cb_addr(struct nl_cache *cache, struct nl_object *ob, int act,
void *data) {
observe_addr_change(act, (struct rtnl_addr *)ob, (struct Listener*)data);
}
static void _e_addr(struct nl_object *ob, void *data) {
observe_addr_change(NL_ACT_NEW, (struct rtnl_addr *)ob, (struct Listener*)data);
}
static void observe_route_change(
int act,
struct rtnl_route *route,
struct Listener* listener)
{
if (listener->exc_typ != NULL || listener->observer == Py_None) {
return;
}
PyObject *data;
char *cdst;
char dstbuf[64];
struct nl_addr* dst = rtnl_route_get_dst(route);
if (dst == NULL || nl_addr_get_len(dst) == 0) {
cdst = "default";
} else {
cdst = nl_addr2str(dst, dstbuf, sizeof(dstbuf));
}
int ifindex = -1;
int nnexthops = rtnl_route_get_nnexthops(route);
if (nnexthops > 0) {
// Bit cheaty to ignore multipath but....
struct rtnl_nexthop* nh = rtnl_route_nexthop_n(route, 0);
ifindex = rtnl_route_nh_get_ifindex(nh);
}
data = Py_BuildValue(
"{sB sB sI sy si}",
"family", rtnl_route_get_family(route),
"type", rtnl_route_get_type(route),
"table", rtnl_route_get_table(route),
"dst", cdst,
"ifindex", ifindex);
if (data == NULL) {
goto exit;
}
PyObject *r = PyObject_CallMethod(listener->observer, "route_change", "sO", act2str(act), data);
Py_XDECREF(r);
exit:
Py_XDECREF(data);
if (PyErr_Occurred()) {
PyErr_Fetch(&listener->exc_typ, &listener->exc_val, &listener->exc_tb);
}
}
static void _cb_route(struct nl_cache *cache, struct nl_object *ob, int act,
void *data) {
observe_route_change(act, (struct rtnl_route *)ob, (struct Listener*)data);
}
static void _e_route(struct nl_object *ob, void *data) {
observe_route_change(NL_ACT_NEW, (struct rtnl_route *)ob, (struct Listener*)data);
}
static void
listener_dealloc(PyObject *self) {
struct Listener* v = (struct Listener*)self;
PyObject_GC_UnTrack(v);
Py_CLEAR(v->observer);
nl_cache_mngr_free(v->mngr);
Py_CLEAR(v->exc_typ);
Py_CLEAR(v->exc_val);
Py_CLEAR(v->exc_tb);
PyObject_GC_Del(v);
}
static int
listener_traverse(PyObject *self, visitproc visit, void *arg)
{
struct Listener* v = (struct Listener*)self;
Py_VISIT(v->observer);
Py_VISIT(v->exc_typ);
Py_VISIT(v->exc_val);
Py_VISIT(v->exc_tb);
return 0;
}
static PyTypeObject ListenerType;
static PyObject *
listener_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
struct nl_cache_mngr *mngr;
int r;
r = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
if (r < 0) {
PyErr_Format(PyExc_MemoryError, "nl_cache_mngr_alloc failed %d", r);
return NULL;
}
struct Listener* listener = (struct Listener*)type->tp_alloc(type, 0);
listener->mngr = mngr;
Py_INCREF(Py_None);
listener->observer = Py_None;
return (PyObject*)listener;
}
static int
listener_init(PyObject *self, PyObject *args, PyObject *kw)
{
PyObject* observer;
char *kwlist[] = {"observer", 0};
if (!PyArg_ParseTupleAndKeywords(args, kw, "O:listener", kwlist, &observer))
return -1;
struct Listener* listener = (struct Listener*)self;
Py_CLEAR(listener->observer);
Py_INCREF(observer);
listener->observer = observer;
return 0;
}
static PyObject*
maybe_restore(struct Listener* listener) {
if (listener->exc_typ != NULL) {
PyErr_Restore(listener->exc_typ, listener->exc_val, listener->exc_tb);
listener->exc_typ = listener->exc_val = listener->exc_tb = NULL;
return NULL;
}
Py_RETURN_NONE;
}
static PyObject*
listener_start(PyObject *self, PyObject* args)
{
struct nl_cache *addr_cache;
struct Listener* listener = (struct Listener*)self;
int r;
r = rtnl_link_alloc_cache(NULL, AF_UNSPEC, &listener->link_cache);
if (r < 0) {
PyErr_Format(PyExc_MemoryError, "rtnl_link_alloc_cache failed %d\n", r);
return NULL;
}
r = nl_cache_mngr_add_cache_v2(listener->mngr, listener->link_cache, _cb_link, listener);
if (r < 0) {
nl_cache_free(listener->link_cache);
listener->link_cache = NULL;
PyErr_Format(PyExc_RuntimeError, "nl_cache_mngr_add_cache failed %d\n", r);
return NULL;
}
r = rtnl_addr_alloc_cache(NULL, &addr_cache);
if (r < 0) {
PyErr_Format(PyExc_MemoryError, "rtnl_link_alloc_cache failed %d\n", r);
return NULL;
}
r = nl_cache_mngr_add_cache(listener->mngr, addr_cache, _cb_addr, listener);
if (r < 0) {
nl_cache_free(addr_cache);
PyErr_Format(PyExc_RuntimeError, "nl_cache_mngr_add_cache failed %d\n", r);
return NULL;
}
r = rtnl_route_alloc_cache(NULL, AF_UNSPEC, 0, &listener->route_cache);
if (r < 0) {
PyErr_Format(PyExc_MemoryError, "rtnl_route_alloc_cache failed %d\n", r);
return NULL;
}
r = nl_cache_mngr_add_cache(listener->mngr, listener->route_cache, _cb_route, listener);
if (r < 0) {
nl_cache_free(listener->route_cache);
PyErr_Format(PyExc_RuntimeError, "nl_cache_mngr_add_cache failed %d\n", r);
return NULL;
}
nl_cache_foreach(listener->link_cache, _e_link, self);
nl_cache_foreach(addr_cache, _e_addr, self);
nl_cache_foreach(listener->route_cache, _e_route, self);
return maybe_restore(listener);
}
static PyObject*
listener_fileno(PyObject *self, PyObject* args)
{
struct Listener* listener = (struct Listener*)self;
return PyLong_FromLong(nl_cache_mngr_get_fd(listener->mngr));
}
static PyObject*
listener_data_ready(PyObject *self, PyObject* args)
{
struct Listener* listener = (struct Listener*)self;
nl_cache_mngr_data_ready(listener->mngr);
return maybe_restore(listener);
}
static PyObject*
listener_set_link_flags(PyObject *self, PyObject* args, PyObject* kw)
{
int ifindex, flags;
char *kwlist[] = {"ifindex", "flags", 0};
if (!PyArg_ParseTupleAndKeywords(args, kw, "ii:set_link_flags", kwlist, &ifindex, &flags))
return NULL;
struct Listener* listener = (struct Listener*)self;
struct rtnl_link *link = rtnl_link_get(listener->link_cache, ifindex);
if (link == NULL) {
PyErr_SetString(PyExc_RuntimeError, "link not found");
return NULL;
}
struct nl_sock* sk = nl_socket_alloc();
if (sk == NULL) {
rtnl_link_put(link);
PyErr_SetString(PyExc_MemoryError, "nl_socket_alloc() failed");
return NULL;
}
int r = nl_connect(sk, NETLINK_ROUTE);
if (r < 0) {
rtnl_link_put(link);
nl_socket_free(sk);
PyErr_Format(PyExc_RuntimeError, "nl_connect failed %d", r);
return NULL;
}
rtnl_link_set_flags(link, flags);
r = rtnl_link_change(sk, link, link, 0);
rtnl_link_put(link);
nl_socket_free(sk);
if (r < 0) {
PyErr_Format(PyExc_RuntimeError, "rtnl_link_change failed %d", r);
return NULL;
}
Py_RETURN_NONE;
}
static PyObject*
listener_unset_link_flags(PyObject *self, PyObject* args, PyObject* kw)
{
int ifindex, flags;
char *kwlist[] = {"ifindex", "flags", 0};
if (!PyArg_ParseTupleAndKeywords(args, kw, "ii:unset_link_flags", kwlist, &ifindex, &flags))
return NULL;
struct Listener* listener = (struct Listener*)self;
struct rtnl_link *link = rtnl_link_get(listener->link_cache, ifindex);
if (link == NULL) {
PyErr_SetString(PyExc_RuntimeError, "link not found");
return NULL;
}
struct nl_sock* sk = nl_socket_alloc();
if (sk == NULL) {
rtnl_link_put(link);
PyErr_SetString(PyExc_MemoryError, "nl_socket_alloc() failed");
return NULL;
}
int r = nl_connect(sk, NETLINK_ROUTE);
if (r < 0) {
rtnl_link_put(link);
nl_socket_free(sk);
PyErr_Format(PyExc_RuntimeError, "nl_connect failed %d", r);
return NULL;
}
rtnl_link_unset_flags(link, flags);
r = rtnl_link_change(sk, link, link, 0);
rtnl_link_put(link);
nl_socket_free(sk);
if (r < 0) {
PyErr_Format(PyExc_RuntimeError, "rtnl_link_change failed %d", r);
return NULL;
}
Py_RETURN_NONE;
}
static PyMethodDef ListenerMethods[] = {
{"start", listener_start, METH_NOARGS, "XXX."},
{"fileno", listener_fileno, METH_NOARGS, "XXX."},
{"data_ready", listener_data_ready, METH_NOARGS, "XXX."},
{"set_link_flags", (PyCFunction)listener_set_link_flags, METH_VARARGS|METH_KEYWORDS, "XXX."},
{"unset_link_flags", (PyCFunction)listener_unset_link_flags, METH_VARARGS|METH_KEYWORDS, "XXX."},
{},
};
static PyTypeObject ListenerType = {
.ob_base = PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "_rtnetlink.listener",
.tp_basicsize = sizeof(struct Listener),
.tp_dealloc = listener_dealloc,
.tp_new = listener_new,
.tp_init = listener_init,
.tp_traverse = listener_traverse,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_methods = ListenerMethods,
};
static struct PyModuleDef rtnetlink_module = {
PyModuleDef_HEAD_INIT,
"_rtnetlink",
};
PyMODINIT_FUNC
PyInit__rtnetlink(void)
{
PyObject *m = PyModule_Create(&rtnetlink_module);
if (m == NULL)
return NULL;
if (PyType_Ready(&ListenerType) < 0)
return NULL;
PyModule_AddObject(m, "listener", (PyObject *)&ListenerType);
return m;
}
|