undns 0.4.0f → 0.4.0h
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/undns_origin +9 -0
- data/bin/update_origins6_dat +127 -0
- data/ext/undns/earth.c +19 -8
- data/ext/undns/hashtable.c +1 -3
- data/ext/undns/originAS.c +105 -2
- data/ext/undns/queue.c +3 -1
- data/ext/undns/radix.c +2 -2
- data/ext/undns/radix6.c +607 -0
- data/ext/undns/radix6.h +72 -0
- data/ext/undns/ruleset.c +2 -1
- data/lib/undns.rb +54 -5
- data/lib/undns/zebra-dump-parser.pl +592 -0
- data/test/test_undns.rb +2 -0
- metadata +8 -2
- data/lib/undns/origins.dat +0 -517715
data/ext/undns/radix6.h
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
#include "nscommon.h"
|
2
|
+
#include <sys/types.h>
|
3
|
+
#include <sys/socket.h>
|
4
|
+
#include <netinet/in.h>
|
5
|
+
#ifndef __LCLINT__
|
6
|
+
#include <arpa/inet.h>
|
7
|
+
#endif
|
8
|
+
#include <stdio.h>
|
9
|
+
|
10
|
+
// #ifdef __linux__
|
11
|
+
// #define ADDR_T in_addr_t
|
12
|
+
// #else
|
13
|
+
#define ADDR6_T struct in6_addr
|
14
|
+
// #endif
|
15
|
+
|
16
|
+
typedef ADDR6_T address6;
|
17
|
+
/* in radix.h typedef unsigned char prefixlen_t; */
|
18
|
+
/* typedef struct patricia6_entry *patricia6_entry; */
|
19
|
+
/* typedef struct patricia6_table *patricia6_table; */
|
20
|
+
|
21
|
+
struct patricia6_entry {
|
22
|
+
address6 destination;
|
23
|
+
prefixlen_t prefixlen;
|
24
|
+
unsigned char position;
|
25
|
+
void *data;
|
26
|
+
struct patricia6_entry *on,*off;
|
27
|
+
};
|
28
|
+
|
29
|
+
struct patricia6_table {
|
30
|
+
/*@null@*/ struct patricia6_entry *routes;
|
31
|
+
/*@null@*/ struct patricia6_entry *default_route;
|
32
|
+
};
|
33
|
+
|
34
|
+
/* externally useful bits */
|
35
|
+
struct patricia6_table *patricia6_new(void);
|
36
|
+
void patricia6_delete(/*@only@*/ struct patricia6_table * p, void (*data_free)(void *));
|
37
|
+
void patricia6_insert(struct patricia6_table *pt, address6 a,
|
38
|
+
unsigned char prefixlen, /*@owned@*/void *data);
|
39
|
+
/*@null@*//* just get the data *//*@dependent@*/
|
40
|
+
void *patricia6_lookup(struct patricia6_table *pt, address6 a);
|
41
|
+
/* get it all */
|
42
|
+
boolean patricia6_lookup_all(struct patricia6_table * pt,
|
43
|
+
address6 a,
|
44
|
+
/*@out@*/ address6 *prefix,
|
45
|
+
/*@out@*/ prefixlen_t *prefixlen,
|
46
|
+
/*@out@*/ void **data);
|
47
|
+
void patricia6_dump(struct patricia6_table *pt, const char *filename);
|
48
|
+
|
49
|
+
/* slightly less useful */
|
50
|
+
/*@null@*//*@dependent@*/
|
51
|
+
struct patricia6_entry *
|
52
|
+
radix6_resolve_route(/*@null@*/
|
53
|
+
struct patricia6_entry *top,
|
54
|
+
address6 destination);
|
55
|
+
void *get_data6(const struct patricia6_entry *r);
|
56
|
+
|
57
|
+
/* somewhat useful bits */
|
58
|
+
void set_data6(struct patricia6_entry *r, void *data);
|
59
|
+
|
60
|
+
void radix6_insert(struct patricia6_entry **p, /*@owned@*//*@partial@*/struct patricia6_entry *r);
|
61
|
+
|
62
|
+
void radix6_remove_route(struct patricia6_table *n,
|
63
|
+
address6 destination,
|
64
|
+
unsigned char prefixlen);
|
65
|
+
|
66
|
+
void radix6_print_routing_table_text(/*@null@*/const struct patricia6_entry *r,
|
67
|
+
FILE *fdout);
|
68
|
+
void radix6_print_routing_table_dot(/*@null@*/const struct patricia6_entry *r,
|
69
|
+
int fd);
|
70
|
+
|
71
|
+
address6 radix6_mask(address6 addr, prefixlen_t prefix);
|
72
|
+
boolean radix6_test_export(address6 addr, prefixlen_t x); // used by check_radix, real thing is static so as to be inlined.
|
data/ext/undns/ruleset.c
CHANGED
@@ -396,8 +396,9 @@ const char *get_attribute(int asn, const char *hostname, enum attributetype attr
|
|
396
396
|
assert(hostname != NULL);
|
397
397
|
memset(&f, 0, sizeof(f)); /* needed when debugging. */
|
398
398
|
if(find_host_convention(&f, asn, hostname)) {
|
399
|
+
const char *returnme;
|
399
400
|
DM("found match for %s\n", hostname);
|
400
|
-
|
401
|
+
returnme = kdlookup((attr == attr_loc) ? f.match->loc : f.match->type,
|
401
402
|
f.posix_regs,
|
402
403
|
hostname);
|
403
404
|
if(returnme == NULL) {
|
data/lib/undns.rb
CHANGED
@@ -1,18 +1,67 @@
|
|
1
1
|
require 'undns/undns'
|
2
2
|
|
3
|
-
# not clear how to accomplish this inside the .so, so at least this component
|
4
|
-
# has to be in ruby.
|
5
3
|
module Undns
|
4
|
+
# sets the ruleset path based on the current directory location.
|
6
5
|
Undns.ruleset_path = File.expand_path('..', __FILE__) + ':' + Undns.ruleset_path
|
6
|
+
|
7
|
+
# not clear how to accomplish this inside the .so, so at least this component
|
8
|
+
# has to be in ruby.
|
9
|
+
|
10
|
+
# The Path namespace separates the file path names
|
11
|
+
# associated with data files that are installed with the
|
12
|
+
# gem (or generated by update_origins_dat).
|
13
|
+
#
|
14
|
+
# You may want to use these path names to record features
|
15
|
+
# of the files used to interpret network measurement data.
|
16
|
+
# For example, tracking the date, size, or md5 of the file
|
17
|
+
# used to process data may be handy, since the network
|
18
|
+
# changes and these files are likely to need revision.
|
19
|
+
#
|
20
|
+
# Note that the Undns module includes {#Ruleset_In_Use}
|
21
|
+
# and {#Origins_In_Use} to find the actual file used, in
|
22
|
+
# case the search path finds such a file in, for example,
|
23
|
+
# your current working directory that will override the
|
24
|
+
# system installed version.
|
7
25
|
module Path
|
26
|
+
# Determines where origins.dat installed with this gem
|
27
|
+
# is or should be. Prints a warning to stderr if not
|
28
|
+
# found in the expected place.
|
29
|
+
#
|
30
|
+
# @return [String] the path of the origins.dat file installed with this gem.
|
8
31
|
def Path.origins_dat
|
9
|
-
File.expand_path('../origins.dat', __FILE__)
|
32
|
+
ret = File.expand_path('../undns/origins.dat', __FILE__)
|
33
|
+
$stderr.puts "WARNING: origins.dat (#{ret}) is missing; run update_origins_dat" unless test(?e, ret) or $0 =~ /update_origins_dat/
|
34
|
+
ret
|
35
|
+
end
|
36
|
+
# Determines where origins6.dat installed with this gem
|
37
|
+
# is or should be. Prints a warning to stderr if not
|
38
|
+
# found in the expected place.
|
39
|
+
#
|
40
|
+
# @return [String] the path of the origins6.dat file installed with this gem.
|
41
|
+
def Path.origins6_dat
|
42
|
+
ret = File.expand_path('../undns/origins6.dat', __FILE__)
|
43
|
+
$stderr.puts "WARNING: origins6.dat (#{ret}) is missing; run update_origins6_dat" unless test(?e, ret) or $0 =~ /update_origins6_dat/
|
44
|
+
ret
|
10
45
|
end
|
46
|
+
# Determines where Conventions.dat installed with this
|
47
|
+
# gem is or should be. Prints a warning to stderr if
|
48
|
+
# not found in the expected place.
|
49
|
+
#
|
50
|
+
# @return [String] the path of the Conventions.dat file installed with this gem.
|
11
51
|
def Path.conventions_dat
|
12
|
-
File.expand_path('../Conventions.dat', __FILE__)
|
52
|
+
ret = File.expand_path('../undns/Conventions.dat', __FILE__)
|
53
|
+
$stderr.puts "WARNING: Conventions.dat (#{ret}) is missing" unless test(?e, ret)
|
54
|
+
ret
|
13
55
|
end
|
56
|
+
# Determines where LocationTable installed with this gem
|
57
|
+
# is or should be. Prints a warning to stderr if not
|
58
|
+
# found in the expected place.
|
59
|
+
#
|
60
|
+
# @return [String] the path of the LocationTable file installed with this gem.
|
14
61
|
def Path.location_table
|
15
|
-
File.expand_path('../undns/LocationTable', __FILE__)
|
62
|
+
ret = File.expand_path('../undns/LocationTable', __FILE__)
|
63
|
+
$stderr.puts "WARNING: LocationTable (#{ret}) is missing" unless test(?e, ret)
|
64
|
+
ret
|
16
65
|
end
|
17
66
|
end
|
18
67
|
end
|
@@ -0,0 +1,592 @@
|
|
1
|
+
#!/usr/bin/perl
|
2
|
+
#
|
3
|
+
# This program is free software; you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License version 2 or later
|
5
|
+
# as published by the Free Software Foundation.
|
6
|
+
#
|
7
|
+
# This work is inspired by the route_btoa.pl program by Craig Labovitz
|
8
|
+
# <labovit@merit.edu>, which is part of the MRT distribution.
|
9
|
+
#
|
10
|
+
# Documentation about the zebra/MRT packet format:
|
11
|
+
# http://www.iana.org/assignments/mrt/mrt.xml
|
12
|
+
# http://tools.ietf.org/html/rfc6396
|
13
|
+
|
14
|
+
use warnings;
|
15
|
+
use strict;
|
16
|
+
require 5.008;
|
17
|
+
|
18
|
+
# only meaningful for message types TABLE_DUMP and TABLE_DUMP_V2
|
19
|
+
# 1: verbose dump 2: AS path 3: origin AS
|
20
|
+
my $format = 3;
|
21
|
+
my $ignore_v6_routes = 0;
|
22
|
+
|
23
|
+
##############################################################################
|
24
|
+
use constant {
|
25
|
+
MSG_BGP => 5, # MRT only
|
26
|
+
MSG_BGP4PLUS => 9, # MRT only
|
27
|
+
MSG_TABLE_DUMP => 12, # dump bgp routes-mrt
|
28
|
+
MSG_TABLE_DUMP_V2 => 13, # RIPE RIS
|
29
|
+
MSG_BGP4MP => 16, # dump bgp all
|
30
|
+
|
31
|
+
BGP4MP_STATE_CHANGE => 0,
|
32
|
+
BGP4MP_MESSAGE => 1,
|
33
|
+
BGP4MP_ENTRY => 2, # deprecated
|
34
|
+
BGP4MP_SNAPSHOT => 3, # deprecated
|
35
|
+
BGP4MP_MESSAGE_AS4 => 4,
|
36
|
+
BGP4MP_STATE_CHANGE_AS4 => 5,
|
37
|
+
BGP4MP_MESSAGE_LOCAL => 6,
|
38
|
+
BGP4MP_MESSAGE_AS4_LOCAL => 7,
|
39
|
+
|
40
|
+
AFI_IP => 1,
|
41
|
+
AFI_IP6 => 2,
|
42
|
+
|
43
|
+
# for TABLE_DUMP_V2
|
44
|
+
INDEX_TABLE => 1,
|
45
|
+
RIB_IPV4_UNICAST => 2,
|
46
|
+
RIB_IPV4_MULTICAST => 3,
|
47
|
+
RIB_IPV6_UNICAST => 4,
|
48
|
+
RIB_IPV6_MULTICAST => 5,
|
49
|
+
RIB_GENERIC => 6,
|
50
|
+
|
51
|
+
BGP_TYPE_OPEN => 1,
|
52
|
+
BGP_TYPE_UPDATE => 2,
|
53
|
+
BGP_TYPE_NOTIFICATION => 3,
|
54
|
+
BGP_TYPE_KEEPALIVE => 4,
|
55
|
+
|
56
|
+
BGP_ATTR_FLAG_EXTLEN => 0x10,
|
57
|
+
|
58
|
+
AS_SET => 1,
|
59
|
+
|
60
|
+
BGP_ATTR_ORIGIN => 1,
|
61
|
+
BGP_ATTR_AS_PATH => 2,
|
62
|
+
BGP_ATTR_NEXT_HOP => 3,
|
63
|
+
BGP_ATTR_MULTI_EXIT_DISC => 4,
|
64
|
+
BGP_ATTR_LOCAL_PREF => 5,
|
65
|
+
BGP_ATTR_ATOMIC_AGGREGATE => 6,
|
66
|
+
BGP_ATTR_AGGREGATOR => 7,
|
67
|
+
BGP_ATTR_COMMUNITIES => 8,
|
68
|
+
BGP_ATTR_ORIGINATOR_ID => 9,
|
69
|
+
BGP_ATTR_CLUSTER_LIST => 10,
|
70
|
+
## BGP_ATTR_DPA => 11,
|
71
|
+
# BGP_ATTR_ADVERTISER => 12,
|
72
|
+
## BGP_ATTR_RCID_PATH => 13,
|
73
|
+
BGP_ATTR_MP_REACH_NLRI => 14,
|
74
|
+
BGP_ATTR_MP_UNREACH_NLRI => 15,
|
75
|
+
BGP_ATTR_EXT_COMMUNITIES => 16,
|
76
|
+
};
|
77
|
+
|
78
|
+
my @BGP_ORIGIN = qw(IGP EGP Incomplete);
|
79
|
+
|
80
|
+
##############################################################################
|
81
|
+
open(INPUT, '-') or die "Could not open INPUT $!\n";
|
82
|
+
|
83
|
+
use constant BUF_READ_SIZE => 4096 * 8;
|
84
|
+
my $buf = '';
|
85
|
+
my $read_done = 0;
|
86
|
+
|
87
|
+
my @BGP_Peers; # used by the TABLE_DUMP_V2 parser
|
88
|
+
|
89
|
+
while (1) {
|
90
|
+
if ($read_done) {
|
91
|
+
last if length $buf == 0;
|
92
|
+
} elsif (length $buf < BUF_READ_SIZE) {
|
93
|
+
my $tmp = '';
|
94
|
+
my $n = sysread(INPUT, $tmp, BUF_READ_SIZE * 2);
|
95
|
+
die "sysread: $!" if not defined $n;
|
96
|
+
$read_done = 1 if $n == 0;
|
97
|
+
$buf .= $tmp;
|
98
|
+
}
|
99
|
+
|
100
|
+
die "short file (empty packet)" if not $buf;
|
101
|
+
my $header = substr($buf, 0, 12, '');
|
102
|
+
my ($time, $type, $subtype, $packet_length) = unpack('N n n N', $header);
|
103
|
+
my $packet = substr($buf, 0, $packet_length, '');
|
104
|
+
die "short file (got " . (length $packet) . " of $packet_length bytes)"
|
105
|
+
if $packet_length != length $packet;
|
106
|
+
|
107
|
+
if ($format == 1) {
|
108
|
+
my ($sec, $min, $hour, $mday, $mon, $year, @junk) = localtime($time);
|
109
|
+
$mon++;
|
110
|
+
$year += 1900;
|
111
|
+
printf("\nTIME: $year-$mon-$mday %02d:%02d:%02d\n", $hour, $min, $sec);
|
112
|
+
}
|
113
|
+
|
114
|
+
decode_mrt_packet(\$packet, $type, $subtype);
|
115
|
+
}
|
116
|
+
exit 0;
|
117
|
+
|
118
|
+
##############################################################################
|
119
|
+
sub decode_mrt_packet {
|
120
|
+
my ($pkt, $type, $subtype) = @_;
|
121
|
+
|
122
|
+
if ($type == MSG_TABLE_DUMP) { ###########################################
|
123
|
+
my $af = $subtype;
|
124
|
+
my $header_format;
|
125
|
+
|
126
|
+
if ($af == AFI_IP) {
|
127
|
+
$header_format = 'n n a4 C C N a4 n n/a';
|
128
|
+
} elsif ($af == AFI_IP6) {
|
129
|
+
return if $ignore_v6_routes;
|
130
|
+
$header_format = 'n n a16 C C N a16 n n/a';
|
131
|
+
} else {
|
132
|
+
warn "TYPE: MSG_TABLE_DUMP/AFI_UNKNOWN_$af\n";
|
133
|
+
return;
|
134
|
+
}
|
135
|
+
|
136
|
+
my ($viewno, $seq_num, $prefix, $prefixlen, undef, $originated,
|
137
|
+
$peerip, $peer_as, $attributes) = unpack($header_format, $$pkt);
|
138
|
+
my $attr = parse_attributes($attributes);
|
139
|
+
|
140
|
+
if ($format == 1) {
|
141
|
+
print "TYPE: MSG_TABLE_DUMP/" .
|
142
|
+
($af == AFI_IP ? 'AFI_IP' : 'AFI_IP6') . "\n"
|
143
|
+
. "VIEW: $viewno SEQUENCE: $seq_num\n"
|
144
|
+
. 'PREFIX: ' . inet_ntop($af, $prefix) . "/$prefixlen\n"
|
145
|
+
. "ORIGINATED: " . localtime($originated) . "\n";
|
146
|
+
print 'FROM: ' . inet_ntop($af, $peerip) . " AS$peer_as\n"
|
147
|
+
if $peer_as;
|
148
|
+
print_verbose_attributes($attr);
|
149
|
+
} elsif ($format == 2) {
|
150
|
+
print inet_ntop($af, $prefix) . "/$prefixlen"
|
151
|
+
. print_aspath($attr->[BGP_ATTR_AS_PATH]) . "\n";
|
152
|
+
} elsif ($format == 3) {
|
153
|
+
print inet_ntop($af, $prefix) . "/$prefixlen "
|
154
|
+
. unpack('n', origin_as($attr->[BGP_ATTR_AS_PATH])) . "\n"
|
155
|
+
if @{$attr->[BGP_ATTR_AS_PATH]};
|
156
|
+
} else { die }
|
157
|
+
} elsif ($type == MSG_TABLE_DUMP_V2) { ###################################
|
158
|
+
my $af;
|
159
|
+
|
160
|
+
if ($subtype == INDEX_TABLE) {
|
161
|
+
my ($collector_id, $view_name, $peers_count, $rest)
|
162
|
+
= unpack('a4 n/a n a*', $$pkt);
|
163
|
+
$view_name ||= '';
|
164
|
+
print "TYPE: MSG_TABLE_DUMP_V2/INDEX_TABLE\n".
|
165
|
+
"ID: " . inet_ntoa($collector_id) .
|
166
|
+
"\nVIEW_NAME: \"$view_name\", PEERS: $peers_count\n"
|
167
|
+
if $format == 1;
|
168
|
+
|
169
|
+
# unpack each peer
|
170
|
+
my $peer_index = 0;
|
171
|
+
while (length $rest > 0) {
|
172
|
+
# parse only the first byte (peer type, a bit field) to
|
173
|
+
# known the length of the other fields of $rest
|
174
|
+
my ($addrv6, $as32) = split(//, unpack('b8', $rest));
|
175
|
+
my $as_size = $as32 ? 4 : 2;
|
176
|
+
my $mformat = 'x N' . ($addrv6 ? 'a16' : 'a4') . "a$as_size a*";
|
177
|
+
|
178
|
+
my ($bgp_id, $peer_ip, $peer_as);
|
179
|
+
($bgp_id, $peer_ip, $peer_as, $rest) = unpack($mformat, $rest);
|
180
|
+
$peer_as = pretty_as($peer_as);
|
181
|
+
|
182
|
+
my $afi = $addrv6 ? AFI_IP6 : AFI_IP;
|
183
|
+
print " PEER $peer_index: ID: $bgp_id, "
|
184
|
+
. inet_ntop($afi, $peer_ip) . ", AS$peer_as\n"
|
185
|
+
if $format == 1;
|
186
|
+
$BGP_Peers[$peer_index++] = [
|
187
|
+
$bgp_id, $afi, $peer_ip, $peer_as, $as_size,
|
188
|
+
];
|
189
|
+
}
|
190
|
+
|
191
|
+
return;
|
192
|
+
} elsif ($subtype == RIB_IPV4_UNICAST) {
|
193
|
+
$af = AFI_IP;
|
194
|
+
} elsif ($subtype == RIB_IPV4_MULTICAST) {
|
195
|
+
return if $ignore_v6_routes;
|
196
|
+
$af = AFI_IP;
|
197
|
+
} elsif ($subtype == RIB_IPV6_UNICAST) {
|
198
|
+
return if $ignore_v6_routes;
|
199
|
+
$af = AFI_IP6;
|
200
|
+
} elsif ($subtype == RIB_IPV6_MULTICAST) {
|
201
|
+
return if $ignore_v6_routes;
|
202
|
+
$af = AFI_IP6;
|
203
|
+
} elsif ($subtype == RIB_GENERIC) {
|
204
|
+
my ($seq_num, $afi, $safi, $nlri) = unpack('N n C a*', $$pkt);
|
205
|
+
#my (?, $entry_count, $rest) = unpack("? n a*", $nlri);
|
206
|
+
print "TYPE: MSG_TABLE_DUMP_V2/RIB_GENERIC\n"
|
207
|
+
. "SEQUENCE: $seq_num\n"
|
208
|
+
. "AFI: $afi, SAFI: $safi, NLRI(" . length($nlri) ."):\n";
|
209
|
+
hexdump($nlri);
|
210
|
+
return;
|
211
|
+
} else {
|
212
|
+
warn "TYPE: MSG_TABLE_DUMP_V2/UNKNOWN_SUBTYPE_$subtype\n";
|
213
|
+
return;
|
214
|
+
}
|
215
|
+
|
216
|
+
my ($seq_num, $prefixlen, $nlri) = unpack('N C a*', $$pkt);
|
217
|
+
my $bytes = int($prefixlen / 8) + ($prefixlen % 8 ? 1 : 0);
|
218
|
+
my ($prefix, $entry_count, $rest) = unpack("a$bytes n a*", $nlri);
|
219
|
+
$prefix .= "\0" x (($af == AFI_IP ? 4 : 16) - $bytes); # pad with NULs
|
220
|
+
|
221
|
+
while (length $rest > 0) {
|
222
|
+
my ($peer_index, $originated, $attributes);
|
223
|
+
($peer_index, $originated, $attributes, $rest)
|
224
|
+
= unpack('n N n/a a*', $rest);
|
225
|
+
my $attr = parse_attributes($attributes,
|
226
|
+
$BGP_Peers[$peer_index]->[4]);
|
227
|
+
|
228
|
+
if ($format == 1) {
|
229
|
+
print "TYPE: MSG_TABLE_DUMP_V2/ENTRY_" .
|
230
|
+
($af == AFI_IP ? 'AFI_IP' : 'AFI_IP6') . "\n"
|
231
|
+
. "SEQUENCE: $seq_num\n"
|
232
|
+
. 'PREFIX: ' . inet_ntop($af, $prefix) . "/$prefixlen\n"
|
233
|
+
. "ORIGINATED: " . localtime($originated) . "\n";
|
234
|
+
my $peer_as = $BGP_Peers[$peer_index]->[3];
|
235
|
+
print 'FROM: ' . inet_ntop($af, $BGP_Peers[$peer_index]->[2])
|
236
|
+
. " AS$peer_as\n";
|
237
|
+
print_verbose_attributes($attr);
|
238
|
+
print "-\n";
|
239
|
+
} elsif ($format == 2) {
|
240
|
+
print inet_ntop($af, $prefix) . "/$prefixlen"
|
241
|
+
. print_aspath($attr->[BGP_ATTR_AS_PATH]) . "\n";
|
242
|
+
} elsif ($format == 3) {
|
243
|
+
print inet_ntop($af, $prefix) . "/$prefixlen "
|
244
|
+
. unpack($BGP_Peers[$peer_index]->[4] == 2 ? 'n' : 'N',
|
245
|
+
origin_as($attr->[BGP_ATTR_AS_PATH])) . "\n"
|
246
|
+
if @{$attr->[BGP_ATTR_AS_PATH]};
|
247
|
+
} else { die }
|
248
|
+
}
|
249
|
+
|
250
|
+
} elsif ($type == MSG_BGP4MP) { ##########################################
|
251
|
+
if ($subtype == BGP4MP_STATE_CHANGE) { #------------------------------
|
252
|
+
my ($srcas, $dstas, $ifidx, $af, $rest) = unpack('nnnn a*', $$pkt);
|
253
|
+
my $unpack_format;
|
254
|
+
|
255
|
+
if ($af == AFI_IP) {
|
256
|
+
$unpack_format = 'a4 a4 n n';
|
257
|
+
} elsif ($af == AFI_IP6) {
|
258
|
+
$unpack_format = 'a16 a16 n n';
|
259
|
+
} else {
|
260
|
+
warn "TYPE: BGP4MP/BGP4MP_STATE_CHANGE AFI_UNKNOWN_$af\n";
|
261
|
+
return;
|
262
|
+
}
|
263
|
+
|
264
|
+
my ($srcip, $dstip, $old_state, $new_state)
|
265
|
+
= unpack($unpack_format, $rest);
|
266
|
+
print "TYPE: BGP4MP/BGP4MP_STATE_CHANGE " .
|
267
|
+
($af == AFI_IP ? 'AFI_IP' : 'AFI_IP6' ) . "\n";
|
268
|
+
print "FROM: " . inet_ntop($af, $srcip) . "\n" if notnull($srcip);
|
269
|
+
print "TO: " . inet_ntop($af, $dstip) . "\n" if notnull($dstip);
|
270
|
+
print "OLD STATE: $old_state NEW STATE: $new_state\n";
|
271
|
+
# state numbers: see RFC 4271, Appendix 1
|
272
|
+
# 1:IDLE 2:CONNECT 3:ACTIVE 4:OPENSENT 5:OPENCONFIRM 6:ESTABLISHED
|
273
|
+
} elsif ($subtype == BGP4MP_MESSAGE or #------------------------------
|
274
|
+
$subtype == BGP4MP_MESSAGE_AS4) {
|
275
|
+
my ($subtype_str, $asn_unpack_format, $asn_length);
|
276
|
+
if ($subtype == BGP4MP_MESSAGE) {
|
277
|
+
$subtype_str = 'BGP4MP_MESSAGE';
|
278
|
+
$asn_unpack_format = 'nnnn a*'; # 16 bit ASNs
|
279
|
+
} else {
|
280
|
+
$subtype_str = 'BGP4MP_MESSAGE_AS4';
|
281
|
+
$asn_unpack_format = 'NNnn a*'; # 32 bit ASNs
|
282
|
+
$asn_length = 4;
|
283
|
+
}
|
284
|
+
my ($srcas, $dstas, $ifidx, $af, $rest) =
|
285
|
+
unpack($asn_unpack_format, $$pkt);
|
286
|
+
|
287
|
+
my $unpack_format;
|
288
|
+
if ($af == AFI_IP) {
|
289
|
+
$unpack_format = 'a4 a4 a*';
|
290
|
+
} elsif ($af == AFI_IP6) {
|
291
|
+
$unpack_format = 'a16 a16 a*';
|
292
|
+
} else {
|
293
|
+
warn "TYPE: BGP4MP/$subtype_str AFI_UNKNOWN_$af\n";
|
294
|
+
return;
|
295
|
+
}
|
296
|
+
|
297
|
+
my ($srcip, $dstip, $bgppkt) = unpack($unpack_format, $rest);
|
298
|
+
print "TYPE: BGP4MP/$subtype_str " .
|
299
|
+
($af == AFI_IP ? 'AFI_IP' : 'AFI_IP6' ) . "\n";
|
300
|
+
print "FROM: " . inet_ntop($af, $srcip) . "\n" if notnull($srcip);
|
301
|
+
print "TO: " . inet_ntop($af, $dstip) . "\n" if notnull($dstip);
|
302
|
+
parse_bgp_packet($bgppkt, $asn_length);
|
303
|
+
} elsif ($subtype == BGP4MP_ENTRY) { #--------------------------------
|
304
|
+
warn "NOT TESTED"; # XXX
|
305
|
+
my ($view, $status, $time_change, $afi, $safi, $next_hop, $prefix,
|
306
|
+
$attributes) = unpack('n n N n C C/a C/a n/a', $$pkt);
|
307
|
+
my $attr = parse_attributes($attributes);
|
308
|
+
|
309
|
+
print "TYPE: BGP4MP/BGP4MP_ENTRY "
|
310
|
+
. ($afi == AFI_IP ? 'AFI_IP' : 'AFI_IP6' ) . "\n";
|
311
|
+
print_verbose_attributes($attr);
|
312
|
+
} else { #------------------------------------------------------------
|
313
|
+
print "TYPE: BGP4MP/BGP4MP_UNKNOWN-$subtype\n";
|
314
|
+
hexdump($$pkt);
|
315
|
+
}
|
316
|
+
} elsif ($type == MSG_BGP4PLUS ###########################################
|
317
|
+
or $type == MSG_BGP) { #############################################
|
318
|
+
my $unpack_format = ($type == MSG_BGP4PLUS)
|
319
|
+
? 'n a16 n a16 n/a n/a a*' : 'n a4 n a4 n/a n/a a*';
|
320
|
+
my $afi = ($type == MSG_BGP4PLUS) ? AFI_IP6 : AFI_IP;
|
321
|
+
my ($srcas, $srcip, $dstas, $dstip, $unf_routes, $attributes, $nlri)
|
322
|
+
= unpack($unpack_format, $$pkt);
|
323
|
+
my $attr = parse_attributes($attributes);
|
324
|
+
|
325
|
+
print "TYPE: TYPE: BGP4MP/BGP4"
|
326
|
+
. ($afi == AFI_IP ? '' : 'PLUS') . "/UPDATE\n";
|
327
|
+
print "FROM: " . inet_ntop($afi, $srcip) . " AS$srcas\n" if $srcas;
|
328
|
+
print "TO: " . inet_ntop($afi, $dstip) . " AS$dstas\n" if $dstas;
|
329
|
+
print_verbose_attributes($attr);
|
330
|
+
print "WITHDRAWN: $_\n" foreach (parse_nlri_prefixes($unf_routes));
|
331
|
+
print "ANNOUNCE: $_\n" foreach (parse_nlri_prefixes($nlri));
|
332
|
+
} else { ################################################################
|
333
|
+
warn "UNKNOWN TYPE: $type SUBTYPE: $subtype\n";
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
sub parse_attributes {
|
338
|
+
my ($attributes, $as_size) = @_;
|
339
|
+
my @attr;
|
340
|
+
|
341
|
+
while (length $attributes > 0) {
|
342
|
+
my ($flags, $type);
|
343
|
+
($flags, $type, $attributes) = unpack('C C a*', $attributes);
|
344
|
+
|
345
|
+
my $attrib; # content of the next attribute
|
346
|
+
if ($flags & BGP_ATTR_FLAG_EXTLEN) {
|
347
|
+
($attrib, $attributes) = unpack('n/a a*', $attributes);
|
348
|
+
} else {
|
349
|
+
($attrib, $attributes) = unpack('C/a a*', $attributes);
|
350
|
+
}
|
351
|
+
|
352
|
+
if ($type == BGP_ATTR_ORIGIN) {
|
353
|
+
$attr[BGP_ATTR_ORIGIN] = unpack('C', $attrib);
|
354
|
+
} elsif ($type == BGP_ATTR_AS_PATH) {
|
355
|
+
$attr[BGP_ATTR_AS_PATH] = [ ];
|
356
|
+
$as_size ||= 2;
|
357
|
+
while (length $attrib > 0) {
|
358
|
+
my ($seg_type, $seg_length);
|
359
|
+
($seg_type, $seg_length, $attrib) = unpack('C C a*', $attrib);
|
360
|
+
my $seg_value = substr($attrib, 0, $seg_length * $as_size, '');
|
361
|
+
push(@{$attr[BGP_ATTR_AS_PATH]},
|
362
|
+
[ $seg_type, [ unpack("(a$as_size)*", $seg_value) ] ]);
|
363
|
+
}
|
364
|
+
} elsif ($type == BGP_ATTR_NEXT_HOP) {
|
365
|
+
$attr[BGP_ATTR_NEXT_HOP] = $attrib; # IPv4
|
366
|
+
} elsif ($type == BGP_ATTR_MULTI_EXIT_DISC) {
|
367
|
+
$attr[BGP_ATTR_MULTI_EXIT_DISC] = $attrib; # 'N'
|
368
|
+
} elsif ($type == BGP_ATTR_LOCAL_PREF) {
|
369
|
+
$attr[BGP_ATTR_LOCAL_PREF] = $attrib; # 'N'
|
370
|
+
} elsif ($type == BGP_ATTR_ATOMIC_AGGREGATE) {
|
371
|
+
$attr[BGP_ATTR_ATOMIC_AGGREGATE] = 1;
|
372
|
+
} elsif ($type == BGP_ATTR_AGGREGATOR) {
|
373
|
+
$attr[BGP_ATTR_AGGREGATOR] = [ unpack('a2 a4', $attrib) ];# N, IPv4
|
374
|
+
} elsif ($type == BGP_ATTR_COMMUNITIES) {
|
375
|
+
$attr[BGP_ATTR_COMMUNITIES] = [ ];
|
376
|
+
while (length $attrib > 0) {
|
377
|
+
my $community = substr($attrib, 0, 4, '');
|
378
|
+
push(@{$attr[BGP_ATTR_COMMUNITIES]}, $community);
|
379
|
+
}
|
380
|
+
} elsif ($type == BGP_ATTR_MP_REACH_NLRI) {
|
381
|
+
# FIXME v2 uses a different format
|
382
|
+
my ($afi, $safi, $next_hop, $rest) = unpack('n C C/a a*', $attrib);
|
383
|
+
|
384
|
+
# XXX how should I deal with all these cases?
|
385
|
+
my $next_hop_len = length $next_hop;
|
386
|
+
my ($next_hop_global_in, $next_hop_global, $next_hop_local);
|
387
|
+
if ($next_hop_len == 4) {
|
388
|
+
$next_hop_global_in = $next_hop;
|
389
|
+
} elsif ($next_hop_len == 12) {
|
390
|
+
my ($rd_high, $rd_low);
|
391
|
+
($rd_high, $rd_low, $next_hop_global_in)
|
392
|
+
= unpack('N N a4', $next_hop);
|
393
|
+
} elsif ($next_hop_len == 16) {
|
394
|
+
$next_hop_global = $next_hop;
|
395
|
+
} elsif ($next_hop_len == 32) {
|
396
|
+
($next_hop_global, $next_hop_local)
|
397
|
+
= unpack('a16 a16', $next_hop);
|
398
|
+
} else { die }
|
399
|
+
|
400
|
+
my $num_snpa;
|
401
|
+
($num_snpa, $rest) = unpack('C a*', $rest);
|
402
|
+
while ($num_snpa-- > 0) {
|
403
|
+
my $snpa;
|
404
|
+
($snpa, $rest) = unpack('C/a a*', $rest);
|
405
|
+
print "|SNPA: "; hexdump($snpa); # XXX
|
406
|
+
}
|
407
|
+
|
408
|
+
if ($format == 1) { # XXX should not print here...
|
409
|
+
print "|AFI: $afi ($safi)\n";
|
410
|
+
print "|NEXT_HOP: " . inet6_ntoa($next_hop_global)
|
411
|
+
. " (LENGTH: $next_hop_len)\n";
|
412
|
+
print "|ANNOUNCE: $_\n" foreach (parse_nlri_prefixes($rest, $afi));
|
413
|
+
}
|
414
|
+
} elsif ($type == BGP_ATTR_MP_UNREACH_NLRI) {
|
415
|
+
my ($afi, $safi, $nlri) = unpack('n C a*', $attrib);
|
416
|
+
|
417
|
+
if ($format == 1) { # XXX
|
418
|
+
print "|WITHDRAWN: $_\n" foreach(parse_nlri_prefixes($nlri, $afi));
|
419
|
+
}
|
420
|
+
} elsif ($type == BGP_ATTR_ORIGINATOR_ID) {
|
421
|
+
$attr[BGP_ATTR_ORIGINATOR_ID] = $attrib; # IPv4
|
422
|
+
} elsif ($type == BGP_ATTR_CLUSTER_LIST) {
|
423
|
+
$attr[BGP_ATTR_CLUSTER_LIST] = $attrib; # IPv4
|
424
|
+
} else {
|
425
|
+
warn "Unknown BGP attribute $type (flags: $flags)\n";
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
429
|
+
return \@attr;
|
430
|
+
}
|
431
|
+
|
432
|
+
sub parse_bgp_packet {
|
433
|
+
my ($bgppkt, $asn_length) = @_;
|
434
|
+
|
435
|
+
my ($marker, $length, $type, $data) = unpack('a16 n C a*', $bgppkt);
|
436
|
+
|
437
|
+
if ($type == BGP_TYPE_OPEN) {
|
438
|
+
my ($version, $as, $hold_time, $bgp_id, $params)
|
439
|
+
= unpack('C n n a4 C/a', $data);
|
440
|
+
# die if $version != 4;
|
441
|
+
print "BGP PACKET TYPE: OPEN\n"
|
442
|
+
. "AS: $as ID: " . inet_ntoa($bgp_id) . "\n"
|
443
|
+
. "HOLD TIME: ${hold_time}s\n";
|
444
|
+
|
445
|
+
# parse BGP OPEN parameters
|
446
|
+
while (length $params > 0) {
|
447
|
+
my ($par_type, $par_value);
|
448
|
+
($par_type, $par_value, $params) = unpack('C C/a a*', $params);
|
449
|
+
if ($par_type == 1) {
|
450
|
+
my ($code, $data) = unpack('C a*', $par_value);
|
451
|
+
print "PARAMETER: AUTH code $code\n";
|
452
|
+
} elsif ($par_type == 2) {
|
453
|
+
my $caps = $par_value;
|
454
|
+
while (length $caps > 0) {
|
455
|
+
my ($cap_code, $cap_value);
|
456
|
+
($cap_code, $cap_value, $caps) = unpack('C C/a a*', $caps);
|
457
|
+
# see http://www.iana.org/assignments/capability-codes
|
458
|
+
print "PARAMETER: CAPABILITY $cap_code\n";
|
459
|
+
}
|
460
|
+
} else {
|
461
|
+
print "PARAMETER: TYPE $par_type (UNKNOWN) "
|
462
|
+
. "LEN: " . length($params) . "\n";
|
463
|
+
hexdump($params);
|
464
|
+
}
|
465
|
+
}
|
466
|
+
} elsif ($type == BGP_TYPE_UPDATE) {
|
467
|
+
print "BGP PACKET TYPE: UPDATE\n";
|
468
|
+
my ($unf_routes, $attributes, $nlri) = unpack('n/a n/a a*', $data);
|
469
|
+
my $attr = parse_attributes($attributes, $asn_length);
|
470
|
+
print_verbose_attributes($attr);
|
471
|
+
print "WITHDRAWN: $_\n" foreach (parse_nlri_prefixes($unf_routes));
|
472
|
+
print "ANNOUNCED: $_\n" foreach (parse_nlri_prefixes($nlri));
|
473
|
+
} elsif ($type == BGP_TYPE_NOTIFICATION) {
|
474
|
+
print "BGP PACKET TYPE: NOTIFICATION\n";
|
475
|
+
my ($error, $suberror, $data) = unpack('C C a*', $data);
|
476
|
+
print "BGP PACKET TYPE: ERROR $error (subcode $suberror)\n";
|
477
|
+
} elsif ($type == BGP_TYPE_KEEPALIVE) {
|
478
|
+
print "BGP PACKET TYPE: KEEPALIVE\n";
|
479
|
+
} else { die }
|
480
|
+
}
|
481
|
+
|
482
|
+
sub parse_nlri_prefixes {
|
483
|
+
my ($nlri, $afi) = @_;
|
484
|
+
$afi ||= AFI_IP;
|
485
|
+
|
486
|
+
my @prefixes;
|
487
|
+
while ($nlri and length $nlri > 0) {
|
488
|
+
my ($len, $prefix);
|
489
|
+
($len, $nlri) = unpack('C a*', $nlri);
|
490
|
+
my $bytes = int($len / 8) + ($len % 8 ? 1 : 0);
|
491
|
+
($prefix, $nlri) = unpack("a$bytes a*", $nlri);
|
492
|
+
$prefix .= "\0" x (($afi == AFI_IP ? 4 : 16) - $bytes); # pad with NULs
|
493
|
+
push(@prefixes, inet_ntop($afi, $prefix) . "/$len");
|
494
|
+
}
|
495
|
+
return @prefixes;
|
496
|
+
}
|
497
|
+
|
498
|
+
sub print_verbose_attributes {
|
499
|
+
my ($attr) = @_;
|
500
|
+
|
501
|
+
print 'ORIGIN: ' . $BGP_ORIGIN[$attr->[BGP_ATTR_ORIGIN]] . "\n"
|
502
|
+
if defined $attr->[BGP_ATTR_ORIGIN];
|
503
|
+
print 'AS_PATH:' . print_aspath($attr->[BGP_ATTR_AS_PATH]) . "\n"
|
504
|
+
if $attr->[BGP_ATTR_AS_PATH];
|
505
|
+
print 'NEXT_HOP: ' . inet_ntoa($attr->[BGP_ATTR_NEXT_HOP])."\n"
|
506
|
+
if notnull($attr->[BGP_ATTR_NEXT_HOP]);
|
507
|
+
print 'MULTI_EXIT_DISC: ' . unpack('N', $attr->[BGP_ATTR_MULTI_EXIT_DISC])
|
508
|
+
. "\n" if $attr->[BGP_ATTR_MULTI_EXIT_DISC];
|
509
|
+
print "ATOMIC_AGGREGATE\n" if $attr->[BGP_ATTR_ATOMIC_AGGREGATE];
|
510
|
+
print 'AGGREGATOR: ' . unpack('n', ${$attr->[BGP_ATTR_AGGREGATOR]}[0])
|
511
|
+
. ' ' . inet_ntoa(${$attr->[BGP_ATTR_AGGREGATOR]}[1]) . "\n"
|
512
|
+
if $attr->[BGP_ATTR_AGGREGATOR];
|
513
|
+
print 'ORIGINATOR_ID: ' . inet_ntoa($attr->[BGP_ATTR_ORIGINATOR_ID])."\n"
|
514
|
+
if notnull($attr->[BGP_ATTR_ORIGINATOR_ID]);
|
515
|
+
print 'CLUSTER_LIST: ' . inet_ntoa($attr->[BGP_ATTR_CLUSTER_LIST])."\n"
|
516
|
+
if notnull($attr->[BGP_ATTR_CLUSTER_LIST]);
|
517
|
+
print "COMMUNITIES: "
|
518
|
+
. print_communities(@{$attr->[BGP_ATTR_COMMUNITIES]}) . "\n"
|
519
|
+
if $attr->[BGP_ATTR_COMMUNITIES];
|
520
|
+
}
|
521
|
+
|
522
|
+
sub print_communities {
|
523
|
+
my @communities;
|
524
|
+
foreach my $community (@_) {
|
525
|
+
my ($hi, $low) = unpack('n n', $community);
|
526
|
+
push(@communities, "${hi}:${low}");
|
527
|
+
}
|
528
|
+
|
529
|
+
return join(' ', @communities);
|
530
|
+
}
|
531
|
+
|
532
|
+
sub pretty_as {
|
533
|
+
my ($as_hi, $as_lo) = unpack('nn', $_[0]);
|
534
|
+
return defined $as_lo ? ($as_hi ? unpack('N', $_[0]) : $as_lo) : $as_hi;
|
535
|
+
}
|
536
|
+
|
537
|
+
sub print_aspath {
|
538
|
+
my ($aspath) = @_;
|
539
|
+
|
540
|
+
my $s = '';
|
541
|
+
foreach (@$aspath) {
|
542
|
+
# 1 AS_SET 2 AS_SEQUENCE 3 AS_CONFED_SEQUENCE 4 AS_CONFED_SET
|
543
|
+
my ($type, $segment) = @$_;
|
544
|
+
my $s1 = $type == AS_SET ? '{' : '';
|
545
|
+
my $s2 = $type == AS_SET ? '}' : '';
|
546
|
+
my $s3 = $type == AS_SET ? ',' : ' ';
|
547
|
+
$s .= " $s1" . join($s3, map { pretty_as($_) } @$segment) . $s2;
|
548
|
+
}
|
549
|
+
return $s;
|
550
|
+
}
|
551
|
+
|
552
|
+
sub origin_as {
|
553
|
+
my ($aspath) = @_;
|
554
|
+
|
555
|
+
# BEWARE: in presence of an AS_SET the first AS of the set is returned
|
556
|
+
return pop @{ @{pop @{$aspath}}[1] };
|
557
|
+
}
|
558
|
+
|
559
|
+
sub notnull {
|
560
|
+
return 1 if $_[0] and $_[0] ne "\0\0\0\0";
|
561
|
+
return 0;
|
562
|
+
}
|
563
|
+
|
564
|
+
sub inet_ntop {
|
565
|
+
my ($af, $addr) = @_;
|
566
|
+
|
567
|
+
if ($af == AFI_IP) {
|
568
|
+
return inet_ntoa($addr);
|
569
|
+
} elsif ($af == AFI_IP6) {
|
570
|
+
return inet6_ntoa($addr);
|
571
|
+
} else { die }
|
572
|
+
}
|
573
|
+
|
574
|
+
sub inet_ntoa {
|
575
|
+
join('.', unpack('C4', $_[0]));
|
576
|
+
}
|
577
|
+
|
578
|
+
sub inet6_ntoa {
|
579
|
+
local $_ = sprintf("%0*v2x", ':', $_[0]);
|
580
|
+
s/(..):(..)/${1}${2}/g;
|
581
|
+
s/(:0000)+$/::/;
|
582
|
+
return '::' if $_ eq '0000::';
|
583
|
+
return $_;
|
584
|
+
}
|
585
|
+
|
586
|
+
sub hexdump {
|
587
|
+
local $_ = sprintf("%0*v2X", ' ', $_[0]);
|
588
|
+
s/((?:.. ){16})/${1}\n /g;
|
589
|
+
s/((?:.. ){8})/${1} /g;
|
590
|
+
print " $_\n";
|
591
|
+
}
|
592
|
+
|