summaryrefslogtreecommitdiff
path: root/src/libsystemd/sd-netlink/netlink-types.c
blob: 051dac95c21c9e8979b90b472bac2a4ebcaf79e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <linux/netlink.h>

#include "netlink-genl.h"
#include "netlink-internal.h"
#include "netlink-types-internal.h"

static const NLType empty_types[1] = {
        /* fake array to avoid .types==NULL, which denotes invalid type-systems */
};

DEFINE_TYPE_SYSTEM(empty);

static const NLType error_types[] = {
        [NLMSGERR_ATTR_MSG]  = { .type = NETLINK_TYPE_STRING },
        [NLMSGERR_ATTR_OFFS] = { .type = NETLINK_TYPE_U32 },
};

DEFINE_TYPE_SYSTEM(error);

static const NLType basic_types[] = {
        [NLMSG_DONE]  = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system },
        [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &error_type_system, .size = sizeof(struct nlmsgerr) },
};

DEFINE_TYPE_SYSTEM(basic);

uint16_t type_get_type(const NLType *type) {
        assert(type);
        return type->type;
}

size_t type_get_size(const NLType *type) {
        assert(type);
        return type->size;
}

const NLTypeSystem *type_get_type_system(const NLType *nl_type) {
        assert(nl_type);
        assert(nl_type->type == NETLINK_TYPE_NESTED);
        assert(nl_type->type_system);
        return nl_type->type_system;
}

const NLTypeSystemUnion *type_get_type_system_union(const NLType *nl_type) {
        assert(nl_type);
        assert(nl_type->type == NETLINK_TYPE_UNION);
        assert(nl_type->type_system_union);
        return nl_type->type_system_union;
}

int type_system_root_get_type_system_and_header_size(
                sd_netlink *nl,
                uint16_t type,
                const NLTypeSystem **ret_type_system,
                size_t *ret_header_size) {

        const NLType *nl_type;

        assert(nl);

        if (IN_SET(type, NLMSG_DONE, NLMSG_ERROR))
                nl_type = type_system_get_type(&basic_type_system, type);
        else
                switch(nl->protocol) {
                case NETLINK_ROUTE:
                        nl_type = rtnl_get_type(type);
                        break;
                case NETLINK_NETFILTER:
                        nl_type = nfnl_get_type(type);
                        break;
                case NETLINK_GENERIC:
                        return genl_get_type_system_and_header_size(nl, type, ret_type_system, ret_header_size);
                default:
                        return -EOPNOTSUPP;
                }
        if (!nl_type)
                return -EOPNOTSUPP;

        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
                return -EOPNOTSUPP;

        if (ret_type_system)
                *ret_type_system = type_get_type_system(nl_type);
        if (ret_header_size)
                *ret_header_size = type_get_size(nl_type);
        return 0;
}

const NLType *type_system_get_type(const NLTypeSystem *type_system, uint16_t type) {
        const NLType *nl_type;

        assert(type_system);
        assert(type_system->types);

        if (type >= type_system->count)
                return NULL;

        nl_type = &type_system->types[type];

        if (nl_type->type == NETLINK_TYPE_UNSPEC)
                return NULL;

        return nl_type;
}

const NLTypeSystem *type_system_get_type_system(const NLTypeSystem *type_system, uint16_t type) {
        const NLType *nl_type;

        nl_type = type_system_get_type(type_system, type);
        if (!nl_type)
                return NULL;

        return type_get_type_system(nl_type);
}

const NLTypeSystemUnion *type_system_get_type_system_union(const NLTypeSystem *type_system, uint16_t type) {
        const NLType *nl_type;

        nl_type = type_system_get_type(type_system, type);
        if (!nl_type)
                return NULL;

        return type_get_type_system_union(nl_type);
}

NLMatchType type_system_union_get_match_type(const NLTypeSystemUnion *type_system_union) {
        assert(type_system_union);
        return type_system_union->match_type;
}

uint16_t type_system_union_get_match_attribute(const NLTypeSystemUnion *type_system_union) {
        assert(type_system_union);
        assert(type_system_union->match_type == NL_MATCH_SIBLING);
        return type_system_union->match_attribute;
}

const NLTypeSystem *type_system_union_get_type_system_by_string(const NLTypeSystemUnion *type_system_union, const char *key) {
        assert(type_system_union);
        assert(type_system_union->elements);
        assert(type_system_union->match_type == NL_MATCH_SIBLING);
        assert(key);

        for (size_t i = 0; i < type_system_union->count; i++)
                if (streq(type_system_union->elements[i].name, key))
                        return &type_system_union->elements[i].type_system;

        return NULL;
}

const NLTypeSystem *type_system_union_get_type_system_by_protocol(const NLTypeSystemUnion *type_system_union, uint16_t protocol) {
        assert(type_system_union);
        assert(type_system_union->elements);
        assert(type_system_union->match_type == NL_MATCH_PROTOCOL);

        for (size_t i = 0; i < type_system_union->count; i++)
                if (type_system_union->elements[i].protocol == protocol)
                        return &type_system_union->elements[i].type_system;

        return NULL;
}