1/* $NetBSD: label.c,v 1.2 2010/12/09 00:10:59 christos Exp $ */
2
3/*-
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Mihai Chelaru <kefren@NetBSD.org>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <netmpls/mpls.h>
33
34#include <assert.h>
35#include <stdlib.h>
36#include <string.h>
37
38#include "ldp.h"
39#include "tlv_stack.h"
40#include "mpls_routes.h"
41#include "label.h"
42#include "ldp_errors.h"
43
44int	min_label = MIN_LABEL, max_label = MAX_LABEL;
45
46void
47label_init()
48{
49	SLIST_INIT(&label_head);
50}
51
52/*
53 * if binding == 0 it receives a free one
54 */
55struct label   *
56label_add(union sockunion * so_dest, union sockunion * so_pref,
57	  union sockunion * so_gate, uint32_t binding, struct ldp_peer * p,
58	  uint32_t label)
59{
60	struct label   *l;
61	char	spreftmp[INET_ADDRSTRLEN];
62
63	l = calloc(1, sizeof(*l));
64
65	if (!l) {
66		fatalp("label_add: malloc problem\n");
67		return NULL;
68	}
69
70	assert(so_dest);
71	assert(so_pref);
72	assert(so_dest->sa.sa_family == so_pref->sa.sa_family);
73
74	memcpy(&l->so_dest, so_dest, sizeof(union sockunion));
75	memcpy(&l->so_pref, so_pref, sizeof(union sockunion));
76
77	if (so_gate)
78		memcpy(&l->so_gate, so_gate, sizeof(union sockunion));
79	if (binding)
80		l->binding = binding;
81	else
82		l->binding = get_free_local_label();
83	l->p = p;
84	l->label = label;
85
86	SLIST_INSERT_HEAD(&label_head, l, labels);
87
88	strlcpy(spreftmp, union_ntoa(so_pref), INET_ADDRSTRLEN);
89	warnp("[label_add] added binding %d for %s/%s\n", l->binding,
90	    union_ntoa(so_dest), spreftmp);
91
92	send_label_tlv_to_all(&(so_dest->sin.sin_addr),
93	    from_union_to_cidr(so_pref), l->binding);
94	return l;
95}
96
97/* Unlink a label */
98void
99label_del(struct label * l)
100{
101	warnp("[label_del] deleted binding %d for %s\n", l->binding,
102	   union_ntoa(&l->so_dest));
103	SLIST_REMOVE(&label_head, l, label, labels);
104	free(l);
105}
106
107/*
108 * Delete or Reuse the old IPv4 route, delete MPLS route (if any)
109 */
110void
111label_reattach_route(struct label *l, int readd)
112{
113	union sockunion *u;
114	union sockunion emptysu;
115	struct rt_msg rg;
116	int oldbinding = l->binding;
117
118	warnp("[label_reattach_route] binding %d deleted\n",
119		l->binding);
120
121	l->p = NULL;
122	l->binding = MPLS_LABEL_IMPLNULL;
123
124	/* No gateway ? */
125	memset(&emptysu, 0, sizeof (union sockunion));
126	if (memcmp(&l->so_gate, &emptysu, sizeof(union sockunion)) == 0)
127		return;
128
129	if (l->label != MPLS_LABEL_IMPLNULL && readd == LDP_READD_CHANGE) {
130	/* Delete and re-add IPv4 route */
131		if (get_route(&rg, &l->so_dest, &l->so_pref, 1) == LDP_E_OK) {
132			delete_route(&l->so_dest, &l->so_pref, NO_FREESO);
133			add_route(&l->so_dest, &l->so_pref, &l->so_gate, NULL, NULL,
134			    NO_FREESO, RTM_READD);
135		} else if (from_union_to_cidr(&l->so_pref) == 32 &&
136		    l->so_dest.sa.sa_family == AF_INET &&
137		    get_route(&rg, &l->so_dest, NULL, 1) == LDP_E_OK) {
138			delete_route(&l->so_dest, NULL, NO_FREESO);
139			add_route(&l->so_dest, NULL, &l->so_gate, NULL, NULL,
140			    NO_FREESO, RTM_READD);
141		} else
142			add_route(&l->so_dest, &l->so_pref,
143			    &l->so_gate, NULL, NULL, NO_FREESO, RTM_READD);
144	} else
145		if (readd != LDP_READD_NODEL)
146			delete_route(&l->so_dest, &l->so_pref, NO_FREESO);
147
148	l->label = 0;
149
150	/* Deletes pure MPLS route */
151	if (oldbinding >= min_label) {
152		u = make_mpls_union(oldbinding);
153		delete_route(u, NULL, FREESO);
154	}
155}
156/*
157 * Get a label by dst and pref
158 */
159struct label*
160label_get(union sockunion *sodest, union sockunion *sopref)
161{
162	struct label *l;
163
164	SLIST_FOREACH (l, &label_head, labels)
165	    if (sodest->sin.sin_addr.s_addr ==
166		    l->so_dest.sin.sin_addr.s_addr &&
167		sopref->sin.sin_addr.s_addr ==
168		    l->so_pref.sin.sin_addr.s_addr)
169			return l;
170	return NULL;
171}
172
173/*
174 * Find all labels that points to a peer
175 * and reattach them to IPv4
176 */
177void
178label_reattach_all_peer_labels(struct ldp_peer *p, int readd)
179{
180	struct label   *l;
181
182	SLIST_FOREACH(l, &label_head, labels)
183		if (l->p == p)
184			label_reattach_route(l, readd);
185}
186
187/*
188 * Find all labels that points to a peer
189 * and delete them
190 */
191void
192del_all_peer_labels(struct ldp_peer * p, int readd)
193{
194	struct label   *l, *lnext;
195
196	SLIST_FOREACH_SAFE(l, &label_head, labels, lnext) {
197		if(l->p != p)
198			continue;
199		label_reattach_route(l, readd);
200		label_del(l);
201		SLIST_REMOVE(&label_head, l, label, labels);
202	}
203}
204
205/*
206 * Finds a label by its binding and deletes it
207 */
208void
209label_del_by_binding(uint32_t binding, int readd)
210{
211	struct label   *l;
212
213	SLIST_FOREACH(l, &label_head, labels)
214		if ((uint32_t)l->binding == binding) {
215			label_reattach_route(l, readd);
216			label_del(l);
217			SLIST_REMOVE(&label_head, l, label, labels);
218			break;
219		}
220}
221
222/*
223 * For Compatibility with old bindinds code
224 */
225struct label*
226label_get_by_prefix(struct in_addr *a, int prefixlen)
227{
228	union sockunion *so_dest, *so_pref;
229	struct label *l;
230
231	so_dest = make_inet_union(inet_ntoa(*a));
232	so_pref = from_cidr_to_union(prefixlen);
233
234	l = label_get(so_dest, so_pref);
235
236	free(so_dest);
237	free(so_pref);
238
239	return l;
240}
241
242/*
243 * Get a free binding
244 */
245uint32_t
246get_free_local_label()
247{
248	struct label *l;
249	int lbl;
250
251	for (lbl = min_label; lbl <= max_label; lbl++) {
252		SLIST_FOREACH(l, &label_head, labels)
253			if (l->binding == lbl)
254				break;
255		if (l == NULL)
256			return lbl;
257	}
258	return 0;
259}
260
261/*
262 * Change local binding
263 */
264void
265change_local_label(struct label *l, uint32_t newbind)
266{
267	send_withdraw_tlv_to_all(&(l->so_dest.sin.sin_addr),
268		from_union_to_cidr(&(l->so_pref)));
269	l->binding = newbind;
270	send_label_tlv_to_all(&(l->so_dest.sin.sin_addr),
271		from_union_to_cidr(&(l->so_pref)),
272		l->binding);
273}
274