summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_flowspec.c
blob: 6165bf892e7264de4a8ab905d4e49b8e5d9e43c3 (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
// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP FlowSpec for packet handling
 * Portions:
 *     Copyright (C) 2017 ChinaTelecom SDN Group
 *     Copyright (C) 2018 6WIND
 */

#include <zebra.h>
#include <math.h>

#include "prefix.h"
#include "lib_errors.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_route.h"
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_flowspec_util.h"
#include "bgpd/bgp_flowspec_private.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_errors.h"

static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len,
				afi_t afi)
{
	uint32_t offset = 0;
	int type;
	int ret = 0, error = 0;

	while (offset < len-1) {
		type = nlri_content[offset];
		offset++;
		switch (type) {
		case FLOWSPEC_DEST_PREFIX:
		case FLOWSPEC_SRC_PREFIX:
			ret = bgp_flowspec_ip_address(
						BGP_FLOWSPEC_VALIDATE_ONLY,
						nlri_content + offset,
						len - offset, NULL, &error,
						afi, NULL);
			break;
		case FLOWSPEC_FLOW_LABEL:
			if (afi == AFI_IP)
				return -1;
			ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
						   nlri_content + offset,
						   len - offset, NULL, &error);
			break;
		case FLOWSPEC_IP_PROTOCOL:
		case FLOWSPEC_PORT:
		case FLOWSPEC_DEST_PORT:
		case FLOWSPEC_SRC_PORT:
		case FLOWSPEC_ICMP_TYPE:
		case FLOWSPEC_ICMP_CODE:
			ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
						   nlri_content + offset,
						   len - offset, NULL, &error);
			break;
		case FLOWSPEC_TCP_FLAGS:
		case FLOWSPEC_FRAGMENT:
			ret = bgp_flowspec_bitmask_decode(
						   BGP_FLOWSPEC_VALIDATE_ONLY,
						   nlri_content + offset,
						   len - offset, NULL, &error);
			break;
		case FLOWSPEC_PKT_LEN:
		case FLOWSPEC_DSCP:
			ret = bgp_flowspec_op_decode(
						BGP_FLOWSPEC_VALIDATE_ONLY,
						nlri_content + offset,
						len - offset, NULL, &error);
			break;
		default:
			error = -1;
			break;
		}
		offset += ret;
		if (error < 0)
			break;
	}
	return error;
}

int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
			    struct bgp_nlri *packet, bool withdraw)
{
	uint8_t *pnt;
	uint8_t *lim;
	afi_t afi;
	safi_t safi;
	int psize = 0;
	struct prefix p;
	void *temp;

	/* Start processing the NLRI - there may be multiple in the MP_REACH */
	pnt = packet->nlri;
	lim = pnt + packet->length;
	afi = packet->afi;
	safi = packet->safi;

	/*
	 * All other AFI/SAFI's treat no attribute as a implicit
	 * withdraw.  Flowspec should as well.
	 */
	if (!attr)
		withdraw = true;

	if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) {
		flog_err(EC_BGP_FLOWSPEC_PACKET,
			 "BGP flowspec nlri length maximum reached (%u)",
			 packet->length);
		return BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT;
	}

	for (; pnt < lim; pnt += psize) {
		/* Clear prefix structure. */
		memset(&p, 0, sizeof(p));

		/* All FlowSpec NLRI begin with length. */
		if (pnt + 1 > lim)
			return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;

		psize = *pnt++;
		if (psize >= FLOWSPEC_NLRI_SIZELIMIT) {
			psize &= 0x0f;
			psize = psize << 8;
			psize |= *pnt++;
		}
		/* When packet overflow occur return immediately. */
		if (pnt + psize > lim) {
			flog_err(
				EC_BGP_FLOWSPEC_PACKET,
				"Flowspec NLRI length inconsistent ( size %u seen)",
				psize);
			return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
		}

		if (psize == 0) {
			flog_err(EC_BGP_FLOWSPEC_PACKET,
				 "Flowspec NLRI length 0 which makes no sense");
			return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
		}

		if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) {
			flog_err(
				EC_BGP_FLOWSPEC_PACKET,
				"Bad flowspec format or NLRI options not supported");
			return BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT;
		}
		p.family = AF_FLOWSPEC;
		p.prefixlen = 0;
		/* Flowspec encoding is in bytes */
		p.u.prefix_flowspec.prefixlen = psize;
		p.u.prefix_flowspec.family = afi2family(afi);
		temp = XCALLOC(MTYPE_TMP, psize);
		memcpy(temp, pnt, psize);
		p.u.prefix_flowspec.ptr = (uintptr_t) temp;

		if (BGP_DEBUG(flowspec, FLOWSPEC)) {
			char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
			char local_string[BGP_FLOWSPEC_NLRI_STRING_MAX*2+16];
			char ec_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
			char *s = NULL;

			bgp_fs_nlri_get_string((unsigned char *)
					       p.u.prefix_flowspec.ptr,
					       p.u.prefix_flowspec.prefixlen,
					       return_string,
					       NLRI_STRING_FORMAT_MIN, NULL,
					       afi);
			snprintf(ec_string, sizeof(ec_string),
				 "EC{none}");
			if (attr && bgp_attr_get_ecommunity(attr)) {
				s = ecommunity_ecom2str(
					bgp_attr_get_ecommunity(attr),
					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
				snprintf(ec_string, sizeof(ec_string),
					 "EC{%s}",
					s == NULL ? "none" : s);

				if (s)
					ecommunity_strfree(&s);
			}
			snprintf(local_string, sizeof(local_string),
				 "FS Rx %s %s %s %s", withdraw ?
				 "Withdraw":"Update",
				 afi2str(afi), return_string,
				 attr != NULL ? ec_string : "");
			zlog_info("%s", local_string);
		}
		/* Process the route. */
		if (!withdraw) {
			bgp_update(peer, &p, 0, attr, afi, safi,
				   ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL,
				   NULL, 0, 0, NULL);
		} else {
			bgp_withdraw(peer, &p, 0, afi, safi, ZEBRA_ROUTE_BGP,
				     BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL);
		}

		XFREE(MTYPE_TMP, temp);
	}
	return BGP_NLRI_PARSE_OK;
}