summaryrefslogtreecommitdiffstats
path: root/ripngd/ripng_nexthop.c
blob: 882c2fbc8c56182ad4ad7bb3e9107b0a0320c3a0 (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* RIPngd Zebra
 * Copyright (C) 2002 6WIND <vincent.jardin@6wind.com>
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * GNU Zebra is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; see the file COPYING; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* This file is required in order to support properly the RIPng nexthop
 * feature.
 */

#include <zebra.h>

/* For struct udphdr. */
#include <netinet/udp.h>

#include "linklist.h"
#include "stream.h"
#include "log.h"
#include "memory.h"
#include "vty.h"
#include "if.h"
#include "prefix.h"

#include "ripngd/ripngd.h"
#include "ripngd/ripng_debug.h"
#include "ripngd/ripng_nexthop.h"

#define DEBUG 1

#define min(a, b) ((a) < (b) ? (a) : (b))

struct ripng_rte_data {
	struct prefix_ipv6 *p;
	struct ripng_info *rinfo;
	struct ripng_aggregate *aggregate;
};

void _ripng_rte_del(struct ripng_rte_data *A);
int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B);

#define METRIC_OUT(a)                                                          \
	((a)->rinfo ? (a)->rinfo->metric_out : (a)->aggregate->metric_out)
#define NEXTHOP_OUT_PTR(a)                                                     \
	((a)->rinfo ? &((a)->rinfo->nexthop_out)                               \
		    : &((a)->aggregate->nexthop_out))
#define TAG_OUT(a) ((a)->rinfo ? (a)->rinfo->tag_out : (a)->aggregate->tag_out)

struct list *ripng_rte_new(void)
{
	struct list *rte;

	rte = list_new();
	rte->cmp = (int (*)(void *, void *))_ripng_rte_cmp;
	rte->del = (void (*)(void *))_ripng_rte_del;

	return rte;
}

void ripng_rte_free(struct list *ripng_rte_list)
{
	list_delete(&ripng_rte_list);
}

/* Delete RTE */
void _ripng_rte_del(struct ripng_rte_data *A)
{
	XFREE(MTYPE_RIPNG_RTE_DATA, A);
}

/* Compare RTE:
 *  return +  if A > B
 *         0  if A = B
 *         -  if A < B
 */
int _ripng_rte_cmp(struct ripng_rte_data *A, struct ripng_rte_data *B)
{
	return addr6_cmp(NEXTHOP_OUT_PTR(A), NEXTHOP_OUT_PTR(B));
}

/* Add routing table entry */
void ripng_rte_add(struct list *ripng_rte_list, struct prefix_ipv6 *p,
		   struct ripng_info *rinfo, struct ripng_aggregate *aggregate)
{

	struct ripng_rte_data *data;

	/* At least one should not be null */
	assert(!rinfo || !aggregate);

	data = XMALLOC(MTYPE_RIPNG_RTE_DATA, sizeof(*data));
	data->p = p;
	data->rinfo = rinfo;
	data->aggregate = aggregate;

	listnode_add_sort(ripng_rte_list, data);
}

/* Send the RTE with the nexthop support
 */
void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp,
		    struct sockaddr_in6 *to)
{
	struct ripng_interface *ri = ifp->info;
	struct ripng *ripng = ri->ripng;
	struct ripng_rte_data *data;
	struct listnode *node, *nnode;

	struct in6_addr last_nexthop;
	struct in6_addr myself_nexthop;

	struct stream *s;
	int num;
	int mtu;
	int rtemax;
	int ret;

	/* Most of the time, there is no nexthop */
	memset(&last_nexthop, 0, sizeof(last_nexthop));

	/* Use myself_nexthop if the nexthop is not a link-local address,
	 * because
	 * we remain a right path without beeing the optimal one.
	 */
	memset(&myself_nexthop, 0, sizeof(myself_nexthop));

	/* Output stream get from ripng structre.  XXX this should be
	   interface structure. */
	s = ripng->obuf;

	/* Reset stream and RTE counter. */
	stream_reset(s);
	num = 0;

	mtu = ifp->mtu6;
	if (mtu < 0)
		mtu = IFMINMTU;

	rtemax = (min(mtu, RIPNG_MAX_PACKET_SIZE) - IPV6_HDRLEN
		  - sizeof(struct udphdr) - sizeof(struct ripng_packet)
		  + sizeof(struct rte))
		 / sizeof(struct rte);

	for (ALL_LIST_ELEMENTS(ripng_rte_list, node, nnode, data)) {
		/* (2.1) Next hop support */
		if (!IPV6_ADDR_SAME(&last_nexthop, NEXTHOP_OUT_PTR(data))) {

			/* A nexthop entry should be at least followed by 1 RTE
			 */
			if (num == (rtemax - 1)) {
				ret = ripng_send_packet((caddr_t)STREAM_DATA(s),
							stream_get_endp(s), to,
							ifp);

				if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
					ripng_packet_dump(
						(struct ripng_packet *)
							STREAM_DATA(s),
						stream_get_endp(s), "SEND");
				num = 0;
				stream_reset(s);
			}

			/* Add the nexthop (2.1) */

			/* If the received next hop address is not a link-local
			 * address,
			 * it should be treated as 0:0:0:0:0:0:0:0.
			 */
			if (!IN6_IS_ADDR_LINKLOCAL(NEXTHOP_OUT_PTR(data)))
				last_nexthop = myself_nexthop;
			else
				last_nexthop = *NEXTHOP_OUT_PTR(data);

			num = ripng_write_rte(num, s, NULL, &last_nexthop, 0,
					      RIPNG_METRIC_NEXTHOP);
		} else {
			/* Rewrite the nexthop for each new packet */
			if ((num == 0)
			    && !IPV6_ADDR_SAME(&last_nexthop, &myself_nexthop))
				num = ripng_write_rte(num, s, NULL,
						      &last_nexthop, 0,
						      RIPNG_METRIC_NEXTHOP);
		}
		num = ripng_write_rte(num, s, data->p, NULL, TAG_OUT(data),
				      METRIC_OUT(data));

		if (num == rtemax) {
			ret = ripng_send_packet((caddr_t)STREAM_DATA(s),
						stream_get_endp(s), to, ifp);

			if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
				ripng_packet_dump(
					(struct ripng_packet *)STREAM_DATA(s),
					stream_get_endp(s), "SEND");
			num = 0;
			stream_reset(s);
		}
	}

	/* If unwritten RTE exist, flush it. */
	if (num != 0) {
		ret = ripng_send_packet((caddr_t)STREAM_DATA(s),
					stream_get_endp(s), to, ifp);

		if (ret >= 0 && IS_RIPNG_DEBUG_SEND)
			ripng_packet_dump((struct ripng_packet *)STREAM_DATA(s),
					  stream_get_endp(s), "SEND");
		stream_reset(s);
	}
}