1/*
2 * lib/route/nexthop.c	Routing Nexthop
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup route_obj
14 * @defgroup nexthop Nexthop
15 * @{
16 */
17
18#include <netlink-local.h>
19#include <netlink/netlink.h>
20#include <netlink/utils.h>
21#include <netlink/route/rtnl.h>
22#include <netlink/route/route.h>
23
24/** @cond SKIP */
25#define NH_ATTR_FLAGS   0x000001
26#define NH_ATTR_WEIGHT  0x000002
27#define NH_ATTR_IFINDEX 0x000004
28#define NH_ATTR_GATEWAY 0x000008
29#define NH_ATTR_REALMS  0x000010
30/** @endcond */
31
32/**
33 * @name Allocation/Freeing
34 * @{
35 */
36
37struct rtnl_nexthop *rtnl_route_nh_alloc(void)
38{
39	struct rtnl_nexthop *nh;
40
41	nh = calloc(1, sizeof(*nh));
42	if (!nh)
43		return NULL;
44
45	nl_init_list_head(&nh->rtnh_list);
46
47	return nh;
48}
49
50struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src)
51{
52	struct rtnl_nexthop *nh;
53
54	nh = rtnl_route_nh_alloc();
55	if (!nh)
56		return NULL;
57
58	nh->rtnh_flags = src->rtnh_flags;
59	nh->rtnh_flag_mask = src->rtnh_flag_mask;
60	nh->rtnh_weight = src->rtnh_weight;
61	nh->rtnh_ifindex = src->rtnh_ifindex;
62	nh->ce_mask = src->ce_mask;
63
64	if (src->rtnh_gateway) {
65		nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway);
66		if (!nh->rtnh_gateway) {
67			free(nh);
68			return NULL;
69		}
70	}
71
72	return nh;
73}
74
75void rtnl_route_nh_free(struct rtnl_nexthop *nh)
76{
77	nl_addr_put(nh->rtnh_gateway);
78	free(nh);
79}
80
81/** @} */
82
83int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b,
84			  uint32_t attrs, int loose)
85{
86	int diff = 0;
87
88#define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR)
89
90	diff |= NH_DIFF(IFINDEX,	a->rtnh_ifindex != b->rtnh_ifindex);
91	diff |= NH_DIFF(WEIGHT,		a->rtnh_weight != b->rtnh_weight);
92	diff |= NH_DIFF(REALMS,		a->rtnh_realms != b->rtnh_realms);
93	diff |= NH_DIFF(GATEWAY,	nl_addr_cmp(a->rtnh_gateway,
94						    b->rtnh_gateway));
95
96	if (loose)
97		diff |= NH_DIFF(FLAGS,
98			  (a->rtnh_flags ^ b->rtnh_flags) & b->rtnh_flag_mask);
99	else
100		diff |= NH_DIFF(FLAGS, a->rtnh_flags != b->rtnh_flags);
101
102#undef NH_DIFF
103
104	return diff;
105}
106
107static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
108{
109	struct nl_cache *link_cache;
110	char buf[128];
111
112	link_cache = nl_cache_mngt_require("route/link");
113
114	nl_dump(dp, "via");
115
116	if (nh->ce_mask & NH_ATTR_GATEWAY)
117		nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway,
118						   buf, sizeof(buf)));
119
120	if(nh->ce_mask & NH_ATTR_IFINDEX) {
121		if (link_cache) {
122			nl_dump(dp, " dev %s",
123				rtnl_link_i2name(link_cache,
124						 nh->rtnh_ifindex,
125						 buf, sizeof(buf)));
126		} else
127			nl_dump(dp, " dev %d", nh->rtnh_ifindex);
128	}
129
130	nl_dump(dp, " ");
131}
132
133static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
134{
135	struct nl_cache *link_cache;
136	char buf[128];
137
138	link_cache = nl_cache_mngt_require("route/link");
139
140	nl_dump(dp, "nexthop");
141
142	if (nh->ce_mask & NH_ATTR_GATEWAY)
143		nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway,
144						   buf, sizeof(buf)));
145
146	if(nh->ce_mask & NH_ATTR_IFINDEX) {
147		if (link_cache) {
148			nl_dump(dp, " dev %s",
149				rtnl_link_i2name(link_cache,
150						 nh->rtnh_ifindex,
151						 buf, sizeof(buf)));
152		} else
153			nl_dump(dp, " dev %d", nh->rtnh_ifindex);
154	}
155
156	if (nh->ce_mask & NH_ATTR_WEIGHT)
157		nl_dump(dp, " weight %u", nh->rtnh_weight);
158
159	if (nh->ce_mask & NH_ATTR_REALMS)
160		nl_dump(dp, " realm %04x:%04x",
161			RTNL_REALM_FROM(nh->rtnh_realms),
162			RTNL_REALM_TO(nh->rtnh_realms));
163
164	if (nh->ce_mask & NH_ATTR_FLAGS)
165		nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags,
166							buf, sizeof(buf)));
167}
168
169static void nh_dump_env(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
170{
171	struct nl_cache *link_cache;
172	char buf[128];
173
174	link_cache = nl_cache_mngt_require("route/link");
175
176	if (nh->ce_mask & NH_ATTR_GATEWAY)
177		nl_dump_line(dp, "ROUTE_NH%d_VIA=%s\n", dp->dp_ivar,
178			nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf)));
179
180	if(nh->ce_mask & NH_ATTR_IFINDEX) {
181		if (link_cache) {
182			nl_dump_line(dp, "ROUTE_NH%d_DEV=%s\n", dp->dp_ivar,
183					rtnl_link_i2name(link_cache,
184						 nh->rtnh_ifindex,
185						 buf, sizeof(buf)));
186		} else
187			nl_dump_line(dp, "ROUTE_NH%d_DEV=%d\n", dp->dp_ivar,
188					nh->rtnh_ifindex);
189	}
190
191	if (nh->ce_mask & NH_ATTR_WEIGHT)
192		nl_dump_line(dp, "ROUTE_NH%d_WEIGHT=%u\n", dp->dp_ivar,
193				nh->rtnh_weight);
194
195	if (nh->ce_mask & NH_ATTR_REALMS)
196		nl_dump_line(dp, "ROUTE_NH%d_REALM=%04x:%04x\n", dp->dp_ivar,
197			RTNL_REALM_FROM(nh->rtnh_realms),
198			RTNL_REALM_TO(nh->rtnh_realms));
199
200	if (nh->ce_mask & NH_ATTR_FLAGS)
201		nl_dump_line(dp, "ROUTE_NH%d_FLAGS=<%s>\n", dp->dp_ivar,
202			rtnl_route_nh_flags2str(nh->rtnh_flags,
203							buf, sizeof(buf)));
204}
205void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
206{
207	switch (dp->dp_type) {
208	case NL_DUMP_LINE:
209		nh_dump_line(nh, dp);
210		break;
211
212	case NL_DUMP_DETAILS:
213	case NL_DUMP_STATS:
214		if (dp->dp_ivar == NH_DUMP_FROM_DETAILS)
215			nh_dump_details(nh, dp);
216		break;
217
218	case NL_DUMP_ENV:
219		nh_dump_env(nh, dp);
220		break;
221
222	default:
223		break;
224	}
225}
226
227/**
228 * @name Attributes
229 * @{
230 */
231
232void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight)
233{
234	nh->rtnh_weight = weight;
235	nh->ce_mask |= NH_ATTR_WEIGHT;
236}
237
238uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh)
239{
240	return nh->rtnh_weight;
241}
242
243void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex)
244{
245	nh->rtnh_ifindex = ifindex;
246	nh->ce_mask |= NH_ATTR_IFINDEX;
247}
248
249int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh)
250{
251	return nh->rtnh_ifindex;
252}
253
254void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr)
255{
256	struct nl_addr *old = nh->rtnh_gateway;
257
258	if (addr) {
259		nh->rtnh_gateway = nl_addr_get(addr);
260		nh->ce_mask |= NH_ATTR_GATEWAY;
261	} else {
262		nh->ce_mask &= ~NH_ATTR_GATEWAY;
263		nh->rtnh_gateway = NULL;
264	}
265
266	if (old)
267		nl_addr_put(old);
268}
269
270struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh)
271{
272	return nh->rtnh_gateway;
273}
274
275void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags)
276{
277	nh->rtnh_flag_mask |= flags;
278	nh->rtnh_flags |= flags;
279	nh->ce_mask |= NH_ATTR_FLAGS;
280}
281
282void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags)
283{
284	nh->rtnh_flag_mask |= flags;
285	nh->rtnh_flags &= ~flags;
286	nh->ce_mask |= NH_ATTR_FLAGS;
287}
288
289unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh)
290{
291	return nh->rtnh_flags;
292}
293
294void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms)
295{
296	nh->rtnh_realms = realms;
297	nh->ce_mask |= NH_ATTR_REALMS;
298}
299
300uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh)
301{
302	return nh->rtnh_realms;
303}
304
305/** @} */
306
307/**
308 * @name Nexthop Flags Translations
309 * @{
310 */
311
312static struct trans_tbl nh_flags[] = {
313	__ADD(RTNH_F_DEAD, dead)
314	__ADD(RTNH_F_PERVASIVE, pervasive)
315	__ADD(RTNH_F_ONLINK, onlink)
316};
317
318char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len)
319{
320	return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags));
321}
322
323int rtnl_route_nh_str2flags(const char *name)
324{
325	return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags));
326}
327
328/** @} */
329
330/** @} */
331