AMPR-Rip44
view release on metacpan or search on metacpan
{
my($rkey) = @_;
# This is ugly and slow - we fork /sbin/ip twice for every route change.
# Should talk to the netlink device instead, but this is easier to
# do right now, and good enough for this little routing table.
my($out, $cmd);
$cmd = "LANG=C $routebin route del $rkey";
$out = `$cmd 2>&1`;
if ($?) {
if ($verbose > 1 || $out !~ /No such process/) {
warn "route del failed: '$cmd': $out\n";
}
}
}
# expire old routes
sub expire_routes()
{
warn "expiring old routes\n" if ($verbose);
# now go and update the routing table
route_delete($rkey);
my($out, $cmd);
$cmd = "LANG=C $routebin route add $rkey via $nexthop dev $tunnel_if window $tcp_window onlink";
$out = `$cmd 2>&1\n`;
if ($?) {
warn "route add failed: '$cmd': $out\n";
}
}
# process a RIPv2 password authentication entry
sub process_rip_auth_entry($)
{
my($entry) = @_;
my $e_af = unpack('n', substr($entry, 0, 2));
if ($e_af != 0xFFFF) {
warn "RIPv2 first message does not contain auth password: ignoring\n" if ($verbose);
return 0;
}
my $e_type = unpack('n', substr($entry, 2, 2));
# nexthop address does not point to self
if (defined $my_addresses{$e_nexthop_s}) {
warn "$e_net_s/$e_netmask_s => $e_nexthop_s blocked, local gw\n" if ($verbose);
return (0, 'local gw');
}
return (1, 'ok');
}
# process a RIPv2 route entry
sub process_rip_route_entry($)
{
my($entry) = @_;
my $e_af = unpack('n', substr($entry, 0, 2));
my $e_rtag = unpack('n', substr($entry, 2, 2));
if ($e_af == 0xFFFF) {
process_rip_auth_entry($entry);
return -1;
}
if ($e_af != AF_INET) {
warn "$me: RIPv2 entry has unsupported AF $e_af\n";
return 0;
}
my $e_net = substr($entry, 4, 4);
my $e_net_i = unpack('N', $e_net);
}
warn "entry: af $e_af rtag $e_rtag $e_net_s/$e_netmask_s via $e_nexthop_s metric $e_metric\n" if ($verbose > 1);
# Ok, we have a valid route, consider adding it in the kernel's routing table
consider_route($e_net_s, $e_netmask_s, $e_nexthop_s, $e_rtag);
return 1;
}
# process a RIP message
sub process_msg($$$)
{
my($addr_s, $perr_port, $msg) = @_;
# validate packet's length
if (length($msg) < RIP_HDR_LEN + RIP_ENTRY_LEN) {
warn "$me: ignored too short packet from $addr_s: " . length($msg) . "\n";
return -1;
}
if (length($msg) > RIP_HDR_LEN + RIP_ENTRY_LEN*25) {
}
if ($zero1 != 0 || $zero2 != 0) {
warn "$me: ignored RIP packet from $addr_s: zero bytes are not zero in header\n";
return -1;
}
my $init_msg = 0;
# if password auth is required, require it!
if (defined $rip_passwd) {
return -1 if (!process_rip_auth_entry(substr($entries, 0, RIP_ENTRY_LEN)));
$init_msg += RIP_ENTRY_LEN;
}
# Ok, process the actual route entries
my $routes = 0;
for (my $i = $init_msg; $i < length($entries); $i += RIP_ENTRY_LEN) {
my $entry = substr($entries, $i, RIP_ENTRY_LEN);
my $n = process_rip_route_entry($entry);
return -1 if ($n < 0);
$routes += $n;
}
return $routes;
}
#
####### main #############################################
#
LocalPort => 520,
ReuseAddr => 1,
) or die $!;
$socket->mcast_add('224.0.0.9', $tunnel_if) or die $!;
my $expire_interval = 60*60;
my $next_expire = time() + $expire_interval;
# Main loop: receive broadcasts, check that they're from the correct
# address and port, and pass them on to processing
warn "entering main loop, waiting for RIPv2 datagrams\n" if ($verbose);
while (1) {
my $msg;
my $remote_address = recv($socket, $msg, 1500, 0);
if (!defined $remote_address) {
next;
}
my ($peer_port, $peer_addr) = unpack_sockaddr_in($remote_address);
my $addr_s = inet_ntoa($peer_addr);
if ($addr_s ne '44.0.0.1' || $peer_port ne 520) {
warn "$me: ignored packet from $addr_s: $peer_port: " . length($msg) . "\n";
next;
}
warn "received from $addr_s: $peer_port: " . length($msg) . " bytes\n" if ($verbose);
my $routes = process_msg($addr_s, $peer_port, $msg);
warn "processed $routes route entries\n" if ($verbose && $routes >= 0);
# Consider expiring old routes. This is actually never run if we do not receive
# any RIP broadcasts at all (the recv() is blocking)
# The (desired) side effect is that if the RIP announcer
# dies, the entries do not time out.
if (time() > $next_expire || $next_expire > time() + $expire_interval) {
$next_expire = time() + $expire_interval;
expire_routes();
}
}
( run in 0.269 second using v1.01-cache-2.11-cpan-8d75d55dd25 )