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
|
#!/usr/bin/perl
package OVS;
use strict;
use base qw(Net::DBus::Object);
use Net::DBus::Exporter qw(org.opensuse.os_autoinst.switch);
require IPC::System::Simple;
use autodie qw(:all);
sub new {
my $class = shift;
my $service = shift;
my $self = $class->SUPER::new($service, '/switch');
bless $self, $class;
$self->init_switch;
return $self;
}
sub init_switch {
my $self = shift;
$self->{BRIDGE} = $ENV{OS_AUTOINST_USE_BRIDGE};
$self->{BRIDGE} //= 'br0';
#the bridge must be already created and configured
system('ovs-vsctl', 'br-exists', $self->{BRIDGE});
my $bridge_conf = `ip addr show $self->{BRIDGE}`;
$self->{MAC} = $1 if $bridge_conf =~ /ether\s+(([0-9a-f]{2}:){5}[0-9a-f]{2})\s/;
$self->{IP} = $1 if $bridge_conf =~ /inet\s+(([0-9]+.){3}[0-9]+\/[0-9]+)\s/;
die "can't parse bridge local port MAC" unless $self->{MAC};
die "can't parse bridge local port IP" unless $self->{IP};
# the VM have unique MAC that differs in the last 16 bits (see /usr/lib/os-autoinst/backend/qemu.pm)
# the IP can conflict across vlans
# to allow connection from VM to host os-autoinst (10.0.2.2), we have to do some IP translation
# we use simple scheme:
# MAC 52:54:00:12:XX:YY -> IP 10.1.XX.YY
# br0 has IP 10.0.2.2 and netmask /15 that covers 10.0.0.0 and 10.1.0.0 ranges
# this should be also configured permanently in /etc/sysconfig/network
die "bridge local port IP is expected to be 10.0.2.2/15" unless $self->{IP} eq '10.0.2.2/15';
# openflow rules don't survive reboot so they must be installed on each startup
for my $rule (
# openflow ports:
# LOCAL = br0
# 1,2,3 ... tap devices
# default: normal action
'table=0,priority=0,action=normal',
# reply packets from local port are handled by learned rules in table 1
'table=0,priority=1,in_port=LOCAL,actions=resubmit(,1)',
# arp 10.0.2.2 - learn rule for handling replies, rewrite ARP sender IP to 10.1.x.x range and send to local
# the learned rule rewrites ARP target to the original IP and sends the packet to the original port
'table=0,priority=100,dl_type=0x0806,nw_dst=10.0.2.2,actions=' . #
'learn(table=1,priority=100,in_port=LOCAL,dl_type=0x0806,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],output:NXM_OF_IN_PORT[]),' . #
'load:0xa010000->NXM_OF_ARP_SPA[],move:NXM_OF_ETH_SRC[0..15]->NXM_OF_ARP_SPA[0..15],' . #
'local',
# tcp to $self->{MAC} syn - learn rule for handling replies, rewrite source IP to 10.1.x.x range and send to local
# the learned rule rewrites DST to the original IP and sends the packet to the original port
"table=0,priority=100,dl_type=0x0800,tcp_flags=+syn-ack,dl_dst=$self->{MAC},actions=" . #
'learn(table=1,priority=100,in_port=LOCAL,dl_type=0x0800,NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],load:NXM_OF_IP_SRC[]->NXM_OF_IP_DST[],output:NXM_OF_IN_PORT[]),' . #
'mod_nw_src:10.1.0.0,move:NXM_OF_ETH_SRC[0..15]->NXM_OF_IP_SRC[0..15],' . #
'local',
# tcp to $self->{MAC} other - rewrite source IP to 10.1.x.x range and send to local
"table=0,priority=99,dl_type=0x0800,dl_dst=$self->{MAC},actions=" . #
'mod_nw_src:10.1.0.0,move:NXM_OF_ETH_SRC[0..15]->NXM_OF_IP_SRC[0..15],local',
)
{
system('ovs-ofctl', 'add-flow', $self->{BRIDGE}, $rule);
}
}
dbus_method("set_vlan", ["string", "uint32"]);
sub set_vlan {
my $self = shift;
my $tap = shift;
my $vlan = shift;
if ($tap !~ /^tap[0-9]+$/) {
print STDERR "'$tap' does not fit the naming scheme\n";
return;
}
my $check_bridge = `ovs-vsctl port-to-br $tap`;
chomp $check_bridge;
if ($check_bridge ne $self->{BRIDGE}) {
print STDERR "'$tap' is not connected to bridge '$self->{BRIDGE}'\n";
return;
}
# connect tap device to given vlan
system('ovs-vsctl', 'set', 'port', $tap, "tag=$vlan");
system('ip', 'link', 'set', $tap, 'up');
}
################################################################################
package main;
use strict;
use Net::DBus;
use Net::DBus::Reactor;
my $bus = Net::DBus->system;
my $service = $bus->export_service("org.opensuse.os_autoinst.switch");
my $object = OVS->new($service);
Net::DBus::Reactor->main->run;
exit 0;
|