mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
include ../common.mak
|
||||
|
||||
all: xcec-bridge
|
||||
|
||||
xcec-bridge: xcec-bridge.o
|
||||
|
||||
clean:
|
||||
rm -f *.o core xcec-bridge
|
||||
|
||||
install: ip_watcher.pl xcec-bridge start_hsnc.sh
|
||||
$(SED) -e 's+%S390_TOOLS_VERSION%+$(S390_TOOLS_RELEASE)+' \
|
||||
< start_hsnc.sh >$(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \
|
||||
chown $(OWNER).$(GROUP) $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \
|
||||
chmod 755 $(DESTDIR)$(USRSBINDIR)/start_hsnc.sh; \
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 ip_watcher.pl \
|
||||
$(DESTDIR)$(USRSBINDIR)
|
||||
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 xcec-bridge \
|
||||
$(DESTDIR)$(USRSBINDIR)
|
||||
|
||||
.PHONY: all install clean
|
||||
Executable
+465
@@ -0,0 +1,465 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# ip_watcher.pl - HiperSockets Network Concentrator
|
||||
#
|
||||
# looks for addresses in the HiperSockets and sets them as Proxy ARP on the
|
||||
# OSAs. Also adds routing entries towards the HiperSockets interfaces for
|
||||
# all IP addresses in it
|
||||
|
||||
# $OPERATING_MODE="routing_only";
|
||||
# ip_watcher just takes care of adapting the routing entries. ipv4
|
||||
# forwarding needs to be switched on, if desired mrouted or some multicast
|
||||
# routing daemon should run. ip_watcher also sets the proxy arp entries
|
||||
# of all hsi addresses on the osa device.
|
||||
#
|
||||
# $OPERATING_MODE="full_bridging";
|
||||
# this is like routing_only mode, plus xcec-bridge will bridge all
|
||||
# kinds of traffic (uni-, multi-, broadcast) between the interfaces,
|
||||
# so the stack will not do forwarding.
|
||||
# if interfaces come and go, xcec-bridge will be sent a SIGUSR1.
|
||||
#
|
||||
# $OPERATING_MODE="mc_bridging";
|
||||
# this is a mixture of the above -- ipv4 forwarding of unicast packets
|
||||
# is done by the kernel, multi- and broadcast traffic is bridged by
|
||||
# xcec-bridge.
|
||||
#
|
||||
# $OPERATING_MODE="bc_bridging";
|
||||
# this is another mixture of the above -- ipv4 forwarding of unicast
|
||||
# packets is done by the kernel, multicast is handled by mrouted or some
|
||||
# multicast router, and broadcast traffic is bridged by xcec-bridge.
|
||||
#
|
||||
# Copyright 2017 IBM Corp.
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
|
||||
$OPERATING_MODE="mc_bridging";
|
||||
|
||||
$XCEC_BRIDGE="xcec-bridge";
|
||||
$XCEC_BRIDGE_FULL_PARAM="also_unicast";
|
||||
$XCEC_BRIDGE_MC_PARAM="";
|
||||
$XCEC_BRIDGE_BC_PARAM="only_broadcast";
|
||||
|
||||
$KILLALL="killall";
|
||||
$SIGNAL="-USR1";
|
||||
|
||||
$MASK_PARAM="netmask";
|
||||
$DEV_PARAM="dev";
|
||||
$QETHARP="qetharp -c -q";
|
||||
$ROUTE_ADD_CMD='route add -net ';
|
||||
$ROUTE_DEL_CMD='route del -net ';
|
||||
$PA_ADD_CMD='qethconf parp add -x';
|
||||
$PA_DEL_CMD='qethconf parp del -x';
|
||||
# $PA_ADD_CMD='echo add_rxip4';
|
||||
# $PA_DEL_CMD='echo del_rxip4';
|
||||
|
||||
$CHECK_ONLY="no";
|
||||
|
||||
$nextarg=0;
|
||||
if ($#ARGV>=$nextarg) {
|
||||
if ($ARGV[$nextarg] eq "--check") {
|
||||
$CHECK_ONLY="yes";
|
||||
$nextarg++;
|
||||
}
|
||||
}
|
||||
|
||||
# if there is a parameter to ip_watcher.pl, the parameter will be the
|
||||
# Proxy ARP interface (i.e. the outgoing OSA interface). In this case,
|
||||
# xcec-bridge will not be started, so that only unicast is forwarded.
|
||||
|
||||
# eth0 is default OSA interface
|
||||
if ($#ARGV>=$nextarg) {
|
||||
$PA_INTERFACE=$ARGV[$nextarg];
|
||||
$START_XCEC_BRIDGE="no";
|
||||
} else {
|
||||
$PA_INTERFACE="";
|
||||
$START_XCEC_BRIDGE="yes";
|
||||
}
|
||||
|
||||
$SLEEP_TIME=2;
|
||||
#$TIME_LIMIT=4;
|
||||
#@time_array=(time,time-1,time-2);
|
||||
|
||||
|
||||
sub print_list($@)
|
||||
{
|
||||
my($h)=shift;
|
||||
my(@a)=@_;
|
||||
my($i);
|
||||
foreach $i (@a) {
|
||||
print "DEBUG ". $h .": ". $i ."\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# get outgoing OSA interface (connecting the CECs)
|
||||
sub get_proxy_arp_interface
|
||||
{
|
||||
my($devnos);
|
||||
my($chpid);
|
||||
my($if_name);
|
||||
my($type);
|
||||
my($port);
|
||||
my($chksum);
|
||||
my($prio);
|
||||
my($rtr);
|
||||
my($rest);
|
||||
|
||||
if (opendir(SYSQETH, "/sys/devices/qeth")) {
|
||||
@ALLDEV = grep { /^.+\..+\..+$/ } readdir SYSQETH;
|
||||
closedir SYSQETH;
|
||||
|
||||
foreach $DEV (@ALLDEV) {
|
||||
open(IFNAME, "</sys/devices/qeth/$DEV/if_name") or next;
|
||||
chomp($if_name = readline(IFNAME));
|
||||
close(IFNAME);
|
||||
open(RTR, "</sys/devices/qeth/$DEV/route4") or next;
|
||||
chomp($rtr = readline(RTR));
|
||||
close(RTR);
|
||||
if ( $if_name =~ /^tr|eth.+/ and $rtr =~ /^multicast.+/ ) {
|
||||
$PA_INTERFACE=$if_name;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
die "could not get available qeth interfaces\n";
|
||||
}
|
||||
|
||||
if ($PA_INTERFACE eq "") {
|
||||
die "no multicast router defined or no " .
|
||||
"LAN interface specified as parameter.\n";
|
||||
}
|
||||
}
|
||||
|
||||
# get all interfaces to poll ip addresses from
|
||||
sub update_interface_list
|
||||
{
|
||||
my($devnos);
|
||||
my($chpid);
|
||||
my($if_name);
|
||||
my($type);
|
||||
my($port);
|
||||
my($chksum);
|
||||
my($prio);
|
||||
my($rtr);
|
||||
my($rest);
|
||||
my(@if_list)=();
|
||||
|
||||
if (opendir(SYSQETH, "/sys/devices/qeth")) {
|
||||
@ALLDEV = grep { /^.+\..+\..+$/ } readdir SYSQETH;
|
||||
closedir SYSQETH;
|
||||
|
||||
foreach $DEV (@ALLDEV) {
|
||||
open(IFNAME, "</sys/devices/qeth/$DEV/if_name") or next;
|
||||
chomp($if_name = readline(IFNAME));
|
||||
close(IFNAME);
|
||||
open(RTR, "</sys/devices/qeth/$DEV/route4") or next;
|
||||
chomp($rtr = readline(RTR));
|
||||
close(RTR);
|
||||
if ( $rtr =~ /^.+connector.*/ ) {
|
||||
push(@if_list,$if_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print STDERR "could not get available qeth interfaces\n";
|
||||
return ();
|
||||
}
|
||||
return @if_list
|
||||
}
|
||||
|
||||
# only returns with a maximal frequency
|
||||
sub limit_frequency
|
||||
{
|
||||
# my($t_now)=time;
|
||||
# my($t_last);
|
||||
# my($sleep_time);
|
||||
|
||||
# unshift(@time_array,$t_now);
|
||||
# $t_last=pop(@time_array);
|
||||
# $sleep_time=$TIME_LIMIT-($t_now-$t_last);
|
||||
# if ($sleep_time>0) {
|
||||
# sleep($sleep_time);
|
||||
# }
|
||||
sleep($SLEEP_TIME);
|
||||
}
|
||||
|
||||
# creates a 0x01020304 out of a 1.2.3.4
|
||||
sub convert_ip_string_to_number($)
|
||||
{
|
||||
my($ip_str)=shift;
|
||||
my(@ip);
|
||||
my($ip_oct1);
|
||||
my($ip_oct2);
|
||||
my($ip_oct3);
|
||||
my($ip_oct4);
|
||||
|
||||
@ip=split(/\./,$ip_str);
|
||||
|
||||
# check for parsing error
|
||||
if ($#ip<3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
($ip_oct1,$ip_oct2,$ip_oct3,$ip_oct4)=@ip;
|
||||
|
||||
if ( ($ip_oct1<0) || ($ip_oct1>255) ||
|
||||
($ip_oct2<0) || ($ip_oct2>255) ||
|
||||
($ip_oct3<0) || ($ip_oct3>255) ||
|
||||
($ip_oct4<0) || ($ip_oct4>255) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ($ip_oct1<<24)+($ip_oct2<<16)+($ip_oct3<<8)+($ip_oct4);
|
||||
}
|
||||
|
||||
# returns sorted list of ips (in integer format like __u32) of the interface
|
||||
sub get_ips_on_interface($)
|
||||
{
|
||||
my($interface)=shift;
|
||||
my($cmdline)="$QETHARP $interface |";
|
||||
my(@ip_list)=();
|
||||
my($OUTPUT);
|
||||
my($ip);
|
||||
|
||||
unless (open(OUTPUT,$cmdline)) {
|
||||
print STDERR "can't open $cmdline";
|
||||
return @ip_list;
|
||||
}
|
||||
while (<OUTPUT>) {
|
||||
chop;
|
||||
$ip=convert_ip_string_to_number($_);
|
||||
if ($ip>0) {
|
||||
push(@ip_list,$ip);
|
||||
}
|
||||
}
|
||||
close(OUTPUT) || print STDERR "can't close $cmdline";
|
||||
|
||||
return sort @ip_list;
|
||||
}
|
||||
|
||||
# creates a 1.2.3.4 out of a 0x1020304
|
||||
sub convert_string_to_ip($)
|
||||
{
|
||||
my($ip)=shift;
|
||||
my($ip_oct1);
|
||||
my($ip_oct2);
|
||||
my($ip_oct3);
|
||||
my($ip_oct4);
|
||||
|
||||
$ip_oct4=$ip&0xff;
|
||||
$ip>>=8;
|
||||
$ip_oct3=$ip&0xff;
|
||||
$ip>>=8;
|
||||
$ip_oct2=$ip&0xff;
|
||||
$ip>>=8;
|
||||
$ip_oct1=$ip&0xff;
|
||||
|
||||
return "$ip_oct1.$ip_oct2.$ip_oct3.$ip_oct4";
|
||||
}
|
||||
|
||||
sub __min($$)
|
||||
{
|
||||
my($a)=shift;
|
||||
my($b)=shift;
|
||||
if ($a<$b) {
|
||||
return $a;
|
||||
} else {
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
|
||||
# will create an array of routes in string format
|
||||
sub get_routes_of_ip_list(@)
|
||||
{
|
||||
my(@ip_list)=@_;
|
||||
my(@route_list)=();
|
||||
my($ip);
|
||||
my($ips_left);
|
||||
my($ips_to_combine);
|
||||
my($ip_shifted);
|
||||
my($ips_found);
|
||||
my($end);
|
||||
my($order);
|
||||
my($mask);
|
||||
my($ip_str);
|
||||
my($mask_str);
|
||||
my($ips_fetched);
|
||||
|
||||
while ($#ip_list>=0) {
|
||||
# ips_left is the number of ips left in the list
|
||||
$ips_left=$#ip_list;
|
||||
$ip=shift(@ip_list);
|
||||
$ips_to_combine=1;
|
||||
$ip_shifted=$ip;
|
||||
while ($ip_shifted%2==0) {
|
||||
$ips_to_combine<<=1;
|
||||
$ip_shifted>>=1;
|
||||
# 0 should never be in the list, anyway...
|
||||
if (!$ip_shifted) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
# ips_to_combine is a power of 2 and contains the max number
|
||||
# of entries that could compressed into one route due to its
|
||||
# alignment
|
||||
$end=__min($ips_to_combine-1,$ips_left);
|
||||
$order=1;
|
||||
$ips_found=1;
|
||||
while ($ips_found<=$end) {
|
||||
# ips_found-1, as we have shifted the first ip
|
||||
# already
|
||||
if ($ip_list[$ips_found-1]!=$ip+$ips_found) {
|
||||
last;
|
||||
}
|
||||
$ips_found++;
|
||||
if ($ips_found==2*$order) {
|
||||
$order<<=1;
|
||||
}
|
||||
}
|
||||
# ips_found is now the number of subsequent ips that we can
|
||||
# subsum (one of which is shifted already)
|
||||
$mask=(-$order)&0xffffffff;
|
||||
$ips_fetched=1;
|
||||
while ($ips_fetched<$order) {
|
||||
$ips_fetched++;
|
||||
shift(@ip_list);
|
||||
}
|
||||
$mask_str=convert_string_to_ip($mask);
|
||||
$ip_str=convert_string_to_ip($ip);
|
||||
unshift(@route_list,"$ip_str $MASK_PARAM $mask_str");
|
||||
}
|
||||
|
||||
return @route_list;
|
||||
}
|
||||
|
||||
# will create an array of rxips in string format
|
||||
sub get_pas_of_ip_list(@)
|
||||
{
|
||||
my(@ip_list)=@_;
|
||||
my(@pa_list)=();
|
||||
|
||||
foreach $ip (@ip_list) {
|
||||
unshift(@pa_list,"" . sprintf("%08x",$ip));
|
||||
}
|
||||
|
||||
return @pa_list;
|
||||
}
|
||||
|
||||
sub is_in_list($@)
|
||||
{
|
||||
my($item)=shift;
|
||||
my(@list)=@_;
|
||||
my($i);
|
||||
|
||||
foreach $i (@list) {
|
||||
if ($i eq $item) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub exec_for_diff(@)
|
||||
{
|
||||
my($cmd)=shift;
|
||||
my($new_list,$old_list)=@_;
|
||||
|
||||
foreach $line (@$new_list) {
|
||||
unless (is_in_list($line,@$old_list)) {
|
||||
system($cmd . $line . "> /dev/null 2>&1");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub wait_for_changes()
|
||||
{
|
||||
# blocking ioctl to be informed on SETIP/DELIPs (once it's implemented in
|
||||
# hardware) or sleep for X timeunits
|
||||
}
|
||||
|
||||
sub main()
|
||||
{
|
||||
my(@routes)=();
|
||||
my(@pas)=();
|
||||
my(@new_routes);
|
||||
my(@new_pas);
|
||||
my(@interface_list)=();
|
||||
my(@old_if_list);
|
||||
my($interface);
|
||||
my(@ip_list);
|
||||
my($route);
|
||||
my(@tmp_routes);
|
||||
|
||||
get_proxy_arp_interface();
|
||||
|
||||
if ($CHECK_ONLY eq "yes") {
|
||||
exit 0;
|
||||
}
|
||||
|
||||
if ($START_XCEC_BRIDGE eq "yes") {
|
||||
if ($OPERATING_MODE eq "full_bridging") {
|
||||
system("$XCEC_BRIDGE $XCEC_BRIDGE_FULL_PARAM &")==0 ||
|
||||
die "can't fork $XCEC_BRIDGE: $?";
|
||||
}
|
||||
if ($OPERATING_MODE eq "mc_bridging") {
|
||||
system("$XCEC_BRIDGE $XCEC_BRIDGE_MC_PARAM &")==0 ||
|
||||
die "can't fork $XCEC_BRIDGE: $?";
|
||||
}
|
||||
if ($OPERATING_MODE eq "bc_bridging") {
|
||||
system("$XCEC_BRIDGE $XCEC_BRIDGE_BC_PARAM &")==0 ||
|
||||
die "can't fork $XCEC_BRIDGE: $?";
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if ( ($OPERATING_MODE eq "mc_bridging") ||
|
||||
($OPERATING_MODE eq "bc_bridging") ||
|
||||
($OPERATING_MODE eq "full_bridging") ) {
|
||||
@old_if_list=@interface_list;
|
||||
}
|
||||
@interface_list=update_interface_list();
|
||||
if ( ($OPERATING_MODE eq "mc_bridging") ||
|
||||
($OPERATING_MODE eq "bc_bridging") ||
|
||||
($OPERATING_MODE eq "full_bridging") ) {
|
||||
if ( join(':',@old_if_list) ne
|
||||
join(':',@interface_list) ) {
|
||||
if ($START_XCEC_BRIDGE eq "yes") {
|
||||
system("$KILLALL $SIGNAL $XCEC_BRIDGE")==0 ||
|
||||
print STDERR "can't send signal " .
|
||||
"to $XCEC_BRIDGE to update " .
|
||||
"interfaces.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@new_routes=();
|
||||
@new_pas=();
|
||||
foreach $interface (@interface_list) {
|
||||
@ip_list=get_ips_on_interface($interface);
|
||||
|
||||
@tmp_routes=get_routes_of_ip_list(@ip_list);
|
||||
foreach $route (@tmp_routes) {
|
||||
unshift(@new_routes,
|
||||
"$route $DEV_PARAM $interface");
|
||||
}
|
||||
|
||||
@tmp_pas=get_pas_of_ip_list(@ip_list);
|
||||
foreach $pa (@tmp_pas) {
|
||||
unshift(@new_pas,
|
||||
"$pa $PA_INTERFACE");
|
||||
}
|
||||
}
|
||||
|
||||
exec_for_diff($ROUTE_ADD_CMD,\@new_routes,\@routes);
|
||||
exec_for_diff($ROUTE_DEL_CMD,\@routes,\@new_routes);
|
||||
@routes=@new_routes;
|
||||
|
||||
exec_for_diff($PA_ADD_CMD,\@new_pas,\@pas);
|
||||
exec_for_diff($PA_DEL_CMD,\@pas,\@new_pas);
|
||||
@pas=@new_pas;
|
||||
|
||||
wait_for_changes();
|
||||
limit_frequency();
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# start_hsnc.sh - HiperSockets Network Concentrator
|
||||
#
|
||||
# Wrapper start script for ip_watcher.pl, also cleanup, when ip_watcher.pl
|
||||
# gets killed.
|
||||
#
|
||||
# Copyright IBM Corp. 2003, 2017
|
||||
#
|
||||
# s390-tools is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the MIT license. See LICENSE for details.
|
||||
#
|
||||
|
||||
#
|
||||
# functions
|
||||
#
|
||||
|
||||
function __usage
|
||||
{
|
||||
echo ""
|
||||
echo "For more information about HiperSocket Network Concentrator"
|
||||
echo "please refer to the 'Device Drivers, Features, and Commands'"
|
||||
echo "manual."
|
||||
exit 0
|
||||
|
||||
}
|
||||
|
||||
function PrintVersion
|
||||
{
|
||||
echo "$script_name version %S390_TOOLS_VERSION%"
|
||||
echo "Copyright IBM Corp. 2003, 2017"
|
||||
}
|
||||
#
|
||||
# main
|
||||
#
|
||||
|
||||
script_name="HiperSocket Network Concentrator" # name of this script
|
||||
#
|
||||
# what is the kernel version we are on ?
|
||||
#
|
||||
kernel_version=`uname -r`
|
||||
xcec_bridge="yes"
|
||||
if [ "${kernel_version:0:1}" \> 2 ]; then
|
||||
kernel_version="ok"
|
||||
else
|
||||
if [ "${kernel_version:4:2}" \< 26 ]; then
|
||||
xcec_bridge="no"
|
||||
fi
|
||||
if [ "${kernel_version:2:1}" \> 4 ]; then
|
||||
kernel_version="ok"
|
||||
else
|
||||
echo kernel version too old for this hsnc version.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
#
|
||||
# parse options (currently none avail)
|
||||
#
|
||||
case "$1" in
|
||||
-v | --version ) PrintVersion
|
||||
exit 0 ;;
|
||||
-h | --help ) __usage ;;
|
||||
esac
|
||||
|
||||
if [ X${1}X != XX ] && [ $kernel_version = "ok" ] ; then
|
||||
if ! ls /sys/class/net | grep "^$1$" > /dev/null; then
|
||||
echo interface $1 does not exist.
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ $xcec_bridge = "no" ] ; then
|
||||
echo kernel version too old for this hsnc version.
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
ip_watcher.pl $*
|
||||
|
||||
echo ip_watcher.pl was terminated, cleaning up.
|
||||
|
||||
if [ X${1}X == XX ] ; then
|
||||
echo killing xcec-bridge
|
||||
killall xcec-bridge
|
||||
fi
|
||||
|
||||
echo removing all parp entries from mc interfaces
|
||||
if [ X${1}X == XX ] ; then
|
||||
for DEV in $(ls /sys/devices/qeth/ | egrep '^.+\..+\..+')
|
||||
do
|
||||
if_name=`cat /sys/devices/qeth/$DEV/if_name | sed 's/$/\$/'`
|
||||
rtr=`cat /sys/devices/qeth/$DEV/route4 2> /dev/null | egrep 'multicast'`
|
||||
if [ -n "$rtr" ] ; then
|
||||
echo $if_name >> /tmp/ip_watcher.cleanup1
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo ${1}$ > /tmp/ip_watcher.cleanup1
|
||||
fi
|
||||
|
||||
qethconf rxip list | sed 's/add/del/' | egrep -f /tmp/ip_watcher.cleanup1 > /tmp/ip_watcher.cleanup2
|
||||
|
||||
|
||||
while read line; do
|
||||
qethconf $line > /dev/null 2>&1
|
||||
done < /tmp/ip_watcher.cleanup2
|
||||
rm /tmp/ip_watcher.cleanup1
|
||||
rm /tmp/ip_watcher.cleanup2
|
||||
|
||||
echo removing all routes from connector interfaces
|
||||
for DEV in $(ls /sys/devices/qeth/ | egrep '^.+\..+\..+')
|
||||
do
|
||||
if_name=`cat /sys/devices/qeth/$DEV/if_name | sed 's/$/\$/'`
|
||||
rtr=`cat /sys/devices/qeth/$DEV/route4 2> /dev/null | egrep 'connector'`
|
||||
if [ -n "$rtr" ] ; then
|
||||
echo $if_name >> /tmp/ip_watcher.cleanup1
|
||||
fi
|
||||
done
|
||||
route -n | egrep -f /tmp/ip_watcher.cleanup1 > /tmp/ip_watcher.cleanup2
|
||||
while read line; do
|
||||
route del -net `echo $line | awk '{print $1 " netmask " $3 " dev " $8}'`
|
||||
done < /tmp/ip_watcher.cleanup2
|
||||
rm /tmp/ip_watcher.cleanup1
|
||||
rm /tmp/ip_watcher.cleanup2
|
||||
@@ -0,0 +1,586 @@
|
||||
/*
|
||||
* xcec-bridge - HiperSockets Network Concentrator
|
||||
*
|
||||
* Parameters:
|
||||
* also_unicast - uni-, multi-, broadcast is bridged
|
||||
* <no parameters> - multi-, broadcast is bridged
|
||||
* only broadcast - only broadcast is bridged
|
||||
*
|
||||
* Copyright IBM Corp. 2003, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <features.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
/* a signal causes the interfaces to be re-checked */
|
||||
|
||||
|
||||
#define LOGGING_FACILITY LOG_LOCAL0
|
||||
|
||||
#define UPDATE_SIGNAL SIGUSR1
|
||||
|
||||
#define DEV_NAME_LEN IFNAMSIZ
|
||||
|
||||
#define BUFFER_LEN 65536
|
||||
|
||||
int so_sndbuf=(8*1024*1024);
|
||||
|
||||
int do_unicast_bridging=0;
|
||||
int do_multicast_bridging=1;
|
||||
int do_broadcast_bridging=0;
|
||||
|
||||
struct int_sock {
|
||||
#define I_S_FEATURE_PASSTHROUGH 0x01
|
||||
int features;
|
||||
int i_fd;
|
||||
int o_fd;
|
||||
char dev_name[DEV_NAME_LEN];
|
||||
int mtu_warning;
|
||||
|
||||
struct int_sock *next;
|
||||
};
|
||||
|
||||
fd_set work_fd_set; /* used in and changed by select */
|
||||
|
||||
struct set {
|
||||
fd_set fds;
|
||||
int highest_fd;
|
||||
|
||||
struct int_sock *i_s_list;
|
||||
};
|
||||
|
||||
struct set select_set;
|
||||
|
||||
volatile int update_interface_trigger=0;
|
||||
|
||||
int open_incoming_socket(char *dev_name)
|
||||
{
|
||||
int fd,retval;
|
||||
struct sockaddr_ll sock_addr;
|
||||
struct ifreq if_req;
|
||||
struct packet_mreq mc_req;
|
||||
|
||||
/* we want to receive everything. we filter out stuff that we
|
||||
* don't forward by ourselves */
|
||||
fd=socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
|
||||
if (fd==-1) {
|
||||
syslog(LOG_ERR,"can't open raw packet socket, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(if_req.ifr_name,dev_name,DEV_NAME_LEN);
|
||||
retval=ioctl(fd,SIOCGIFINDEX,&if_req);
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't ioctl on raw packet socket, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sock_addr.sll_protocol=htons(ETH_P_ALL);
|
||||
sock_addr.sll_ifindex=if_req.ifr_ifindex;
|
||||
sock_addr.sll_family=AF_PACKET;
|
||||
retval=bind(fd,(struct sockaddr *)&sock_addr,
|
||||
sizeof(struct sockaddr_ll));
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't bind packet raw packet socket to " \
|
||||
"interface %s -- it will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mc_req.mr_ifindex=if_req.ifr_ifindex;
|
||||
mc_req.mr_type=PACKET_MR_ALLMULTI;
|
||||
mc_req.mr_alen=0;
|
||||
retval=setsockopt(fd,SOL_SOCKET,PACKET_ADD_MEMBERSHIP,
|
||||
&mc_req,sizeof(struct packet_mreq));
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't set socket options to join all " \
|
||||
"multicast groups -- multicast may not be " \
|
||||
"forwarded from %s: %s",dev_name,strerror(errno));
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int open_outgoing_socket(char *dev_name)
|
||||
{
|
||||
int fd,retval;
|
||||
int val;
|
||||
|
||||
fd=socket(PF_INET,SOCK_RAW,IPPROTO_RAW);
|
||||
if (fd==-1) {
|
||||
syslog(LOG_ERR,"can't open raw inet socket, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* IP_HDRINCL should be set by the stack already, we'll set
|
||||
* it nevertheless */
|
||||
val=1;
|
||||
retval=setsockopt(fd,SOL_IP,IP_HDRINCL,&val,sizeof(int));
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't set IP_HDRINCL on raw inet socket, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we bind the socket to the device */
|
||||
retval=setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,
|
||||
dev_name,strlen(dev_name)+1);
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't bind raw inet socket to device, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* get max socket buffer */
|
||||
retval=setsockopt(fd,SOL_SOCKET,SO_SNDBUF,&so_sndbuf,sizeof(int));
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't set socket buffer size, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* and enable broadcast on the socket */
|
||||
val=1;
|
||||
retval=setsockopt(fd,SOL_SOCKET,SO_BROADCAST,&val,sizeof(int));
|
||||
if (retval==-1) {
|
||||
syslog(LOG_ERR,"can't enable broadcast on raw inet socket, " \
|
||||
"interface %s will not be used: %s",
|
||||
dev_name,strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int interface_in_list(struct int_sock *item,struct int_sock *list)
|
||||
{
|
||||
for (;list;list=list->next) {
|
||||
if (!strncmp(item->dev_name,list->dev_name,DEV_NAME_LEN)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int read_sys(struct int_sock **nlist)
|
||||
{
|
||||
DIR *qdir;
|
||||
FILE *qfile;
|
||||
struct dirent *qde;
|
||||
char fname[256];
|
||||
char *tmp;
|
||||
char if_name[256];
|
||||
char rtr[256];
|
||||
struct int_sock *is = NULL;
|
||||
int i;
|
||||
|
||||
qdir = opendir("/sys/devices/qeth");
|
||||
if (!qdir) {
|
||||
syslog(LOG_ERR,"failed to open directory /sys/devices/qeth");
|
||||
return errno;
|
||||
}
|
||||
|
||||
while ((qde = readdir(qdir))) {
|
||||
if ((qde->d_type == DT_DIR) && (qde->d_name[0] != '.')) {
|
||||
strcpy(fname, "/sys/devices/qeth/");
|
||||
strcat(fname, qde->d_name);
|
||||
strcat(fname, "/if_name");
|
||||
qfile = fopen(fname, "r");
|
||||
if (!qfile) {
|
||||
continue;
|
||||
}
|
||||
tmp = if_name;
|
||||
while ((i = fgetc(qfile)) != EOF) {
|
||||
if ((char)i == '\n') {
|
||||
*tmp = 0;
|
||||
break;
|
||||
} else {
|
||||
*tmp = (char)i;
|
||||
}
|
||||
tmp++;
|
||||
}
|
||||
*tmp = 0;
|
||||
fclose(qfile);
|
||||
|
||||
strcpy(fname, "/sys/devices/qeth/");
|
||||
strcat(fname, qde->d_name);
|
||||
strcat(fname, "/route4");
|
||||
qfile = fopen(fname, "r");
|
||||
if (!qfile) {
|
||||
continue;
|
||||
}
|
||||
tmp = rtr;
|
||||
while ((i = fgetc(qfile)) != EOF) {
|
||||
if ((char)i == '\n') {
|
||||
*tmp = 0;
|
||||
break;
|
||||
} else {
|
||||
*tmp = (char)i;
|
||||
}
|
||||
tmp++;
|
||||
}
|
||||
*tmp = 0;
|
||||
fclose(qfile);
|
||||
|
||||
if (!strstr(rtr, "multicast") &&
|
||||
!strstr(rtr, "connector"))
|
||||
continue;
|
||||
|
||||
is = malloc(sizeof(struct int_sock));
|
||||
if (!is) {
|
||||
syslog(LOG_ERR,"no memory while reading from "\
|
||||
"/sys/devices/qeth some interface"\
|
||||
"might not be used");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* as soon as we have one interface echoeing back
|
||||
* broadcasts to us, we don't bridge broadcast
|
||||
* traffic
|
||||
*/
|
||||
if (!(strstr(rtr, "connector+") ||
|
||||
strstr(rtr, "multicast router+")))
|
||||
do_broadcast_bridging=0;
|
||||
|
||||
is->mtu_warning=0;
|
||||
strncpy(is->dev_name, if_name, DEV_NAME_LEN);
|
||||
if (!strncmp(if_name,"hsi",3)) {
|
||||
is->features=I_S_FEATURE_PASSTHROUGH;
|
||||
}
|
||||
|
||||
is->next = *nlist;
|
||||
*nlist = is;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(qdir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void update_interfaces()
|
||||
{
|
||||
struct int_sock *new_list=NULL;
|
||||
struct int_sock *i=NULL,*j,*prev;
|
||||
struct int_sock *new_int=NULL;
|
||||
int i_fd,o_fd;
|
||||
|
||||
/* if all interfaces are '+'-interfaces, we bridge broadcast */
|
||||
do_broadcast_bridging=1;
|
||||
update_interface_trigger=0;
|
||||
syslog(LOG_DEBUG,"rechecking interfaces");
|
||||
|
||||
if (read_sys(&new_list))
|
||||
return;
|
||||
|
||||
for (i=select_set.i_s_list;i;i=i->next) {
|
||||
if (!interface_in_list(i,new_list)) {
|
||||
/* remove interface i */
|
||||
j=select_set.i_s_list;
|
||||
prev=NULL;
|
||||
while (j) {
|
||||
if (!strncmp(j->dev_name,i->dev_name,
|
||||
DEV_NAME_LEN)) {
|
||||
if (!prev) {
|
||||
select_set.i_s_list=j->next;
|
||||
} else {
|
||||
prev->next=j->next;
|
||||
}
|
||||
prev=j;
|
||||
j=j->next;
|
||||
free(j);
|
||||
} else {
|
||||
j=j->next;
|
||||
}
|
||||
}
|
||||
/* and close the socket */
|
||||
close(i->i_fd);
|
||||
close(i->o_fd);
|
||||
syslog(LOG_INFO,"removed interface %s",i->dev_name);
|
||||
}
|
||||
}
|
||||
|
||||
for (i=new_list;i;i=i->next) {
|
||||
if (!interface_in_list(i,select_set.i_s_list)) {
|
||||
/* add interface i */
|
||||
new_int=malloc(sizeof(struct int_sock));
|
||||
if (!new_int) {
|
||||
syslog(LOG_ERR,"can't add interface %s -- " \
|
||||
"no memory",i->dev_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
i_fd=open_incoming_socket(i->dev_name);
|
||||
if (i_fd==-1) {
|
||||
free(new_int);
|
||||
continue;
|
||||
}
|
||||
|
||||
o_fd=open_outgoing_socket(i->dev_name);
|
||||
if (o_fd==-1) {
|
||||
close(i_fd);
|
||||
free(new_int);
|
||||
continue;
|
||||
}
|
||||
|
||||
strncpy(new_int->dev_name,i->dev_name,DEV_NAME_LEN);
|
||||
new_int->i_fd=i_fd;
|
||||
new_int->o_fd=o_fd;
|
||||
new_int->features=i->features;
|
||||
new_int->next=select_set.i_s_list;
|
||||
select_set.i_s_list=new_int;
|
||||
syslog(LOG_INFO,"added interface %s",i->dev_name);
|
||||
}
|
||||
}
|
||||
|
||||
/* kill temporary new_list */
|
||||
while (new_list) {
|
||||
i=new_list->next;
|
||||
free(new_list);
|
||||
new_list=i;
|
||||
}
|
||||
|
||||
/* prepare the fd_set for select */
|
||||
FD_ZERO(&select_set.fds);
|
||||
for (i=select_set.i_s_list;i;i=i->next) {
|
||||
FD_SET(i->i_fd,&select_set.fds);
|
||||
select_set.highest_fd=(i->i_fd>select_set.highest_fd)?
|
||||
i->i_fd:select_set.highest_fd;
|
||||
}
|
||||
}
|
||||
|
||||
void process_packet(struct int_sock *i_s)
|
||||
{
|
||||
int retval;
|
||||
char buffer[BUFFER_LEN];
|
||||
int buffer_len;
|
||||
struct int_sock *i_s_item;
|
||||
struct sockaddr_ll s_ll;
|
||||
struct sockaddr_in s_in;
|
||||
socklen_t sll_len;
|
||||
|
||||
sll_len=(socklen_t)sizeof(struct sockaddr_ll);
|
||||
buffer_len=recvfrom(i_s->i_fd,buffer,BUFFER_LEN,0,
|
||||
(struct sockaddr *)&s_ll,&sll_len);
|
||||
if (buffer_len==-1) {
|
||||
syslog(LOG_WARNING,"recvfrom failed on %s: %s\n",
|
||||
i_s->dev_name,strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* nothing read */
|
||||
if (buffer_len==0)
|
||||
return;
|
||||
|
||||
/* no packets that came from our own stack... that could lead to
|
||||
* traffic loops */
|
||||
if (s_ll.sll_pkttype==PACKET_OUTGOING)
|
||||
return;
|
||||
|
||||
/* only do unicast bridging when required */
|
||||
if ( (s_ll.sll_pkttype==PACKET_HOST) &&
|
||||
(!do_unicast_bridging) )
|
||||
return;
|
||||
|
||||
/* only do multicast bridging when required */
|
||||
if ( (s_ll.sll_pkttype==PACKET_MULTICAST) &&
|
||||
(!do_multicast_bridging) )
|
||||
return;
|
||||
|
||||
/* broadcast is critical, see comment above */
|
||||
if (!do_broadcast_bridging) {
|
||||
if (s_ll.sll_pkttype==PACKET_BROADCAST)
|
||||
return;
|
||||
}
|
||||
|
||||
/* only do v4 at this time */
|
||||
if (s_ll.sll_protocol!=ETH_P_IP)
|
||||
return;
|
||||
|
||||
/* forward buffer to each interface ... */
|
||||
for (i_s_item=select_set.i_s_list;i_s_item;i_s_item=i_s_item->next) {
|
||||
/* ... but i_s */
|
||||
if (i_s_item==i_s) continue;
|
||||
|
||||
s_ll.sll_ifindex=0;
|
||||
s_in.sin_family=AF_INET;
|
||||
s_in.sin_port=0;
|
||||
if (s_ll.sll_pkttype==PACKET_BROADCAST) {
|
||||
s_in.sin_addr.s_addr=INADDR_BROADCAST;
|
||||
} else {
|
||||
memcpy(&s_in.sin_addr, &buffer[16 + ETH_HLEN], 4);
|
||||
}
|
||||
|
||||
retval=sendto(i_s_item->o_fd, buffer + ETH_HLEN,
|
||||
buffer_len - ETH_HLEN, 0,
|
||||
(struct sockaddr *)&s_in,
|
||||
sizeof(struct sockaddr_in));
|
||||
if (retval==-1) {
|
||||
if ( (errno==EMSGSIZE) && (!i_s_item->mtu_warning) ) {
|
||||
syslog(LOG_WARNING,"MTU of %s too small " \
|
||||
"to forward packet with size of %i" \
|
||||
" -- won't show warning again.",
|
||||
i_s_item->dev_name,buffer_len);
|
||||
i_s_item->mtu_warning=1;
|
||||
} else {
|
||||
syslog(LOG_WARNING,"sendto failed on %s: " \
|
||||
"%s\n",i_s_item->dev_name,
|
||||
strerror(errno));
|
||||
}
|
||||
} else if (retval != (buffer_len - ETH_HLEN)) {
|
||||
syslog(LOG_WARNING,"sendto sent only %i instead " \
|
||||
"of %i bytes on %s\n",
|
||||
retval,buffer_len,i_s->dev_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void action_handler(int UNUSED(s))
|
||||
{
|
||||
update_interface_trigger=1;
|
||||
syslog(LOG_DEBUG,"signal caught");
|
||||
/* select will return, interfaces will be re-checked */
|
||||
}
|
||||
|
||||
int main(int argc,char *argv[]) {
|
||||
struct int_sock *i_s;
|
||||
int retval,r;
|
||||
struct sigaction s_a;
|
||||
|
||||
if ( (argc>1) && (!strncmp(argv[1],"also_unicast",12)) ) {
|
||||
do_unicast_bridging=1;
|
||||
} else
|
||||
if ( (argc>1) && (!strncmp(argv[1],"only_broadcast",14)) ) {
|
||||
do_multicast_bridging=0;
|
||||
}
|
||||
|
||||
openlog("xcec-bridge",LOG_NDELAY,LOGGING_FACILITY);
|
||||
|
||||
FD_ZERO(&select_set.fds);
|
||||
select_set.i_s_list=NULL;
|
||||
select_set.highest_fd=0;
|
||||
|
||||
s_a.sa_handler=action_handler;
|
||||
if (sigemptyset(&s_a.sa_mask)) {
|
||||
syslog(LOG_ERR,"problem in sigemptyset: %s -- exiting",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
s_a.sa_flags=0;
|
||||
retval=sigaction(UPDATE_SIGNAL,&s_a,NULL);
|
||||
|
||||
if (sigemptyset(&s_a.sa_mask)) {
|
||||
syslog(LOG_ERR,"problem in sigemptyset: %s -- exiting",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
if (sigaddset(&s_a.sa_mask,UPDATE_SIGNAL)) {
|
||||
syslog(LOG_ERR,"problem in sigaddset: %s -- exiting %s",
|
||||
argv[0],strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
r=sigprocmask(SIG_BLOCK,&s_a.sa_mask,NULL);
|
||||
if (r) {
|
||||
syslog(LOG_ERR,"sigprocmask: %s",strerror(errno));
|
||||
}
|
||||
|
||||
syslog(LOG_INFO,"*** started ***");
|
||||
|
||||
update_interfaces();
|
||||
|
||||
while (1) {
|
||||
r=sigprocmask(SIG_UNBLOCK,&s_a.sa_mask,NULL);
|
||||
if (r) {
|
||||
/* while blocked: */
|
||||
update_interfaces();
|
||||
|
||||
syslog(LOG_INFO,"sigprocmask (unblock): %s",
|
||||
strerror(errno));
|
||||
/* try until sigprocmask is not interrupted by a
|
||||
* signal */
|
||||
while (sigprocmask(SIG_UNBLOCK,&s_a.sa_mask,NULL)) ;
|
||||
}
|
||||
|
||||
memcpy(&work_fd_set,&select_set.fds,sizeof(fd_set));
|
||||
retval=select(select_set.highest_fd+1,&work_fd_set,
|
||||
NULL,NULL,NULL);
|
||||
r=sigprocmask(SIG_BLOCK,&s_a.sa_mask,NULL);
|
||||
if (r) {
|
||||
syslog(LOG_INFO,"sigprocmask (block): %s",
|
||||
strerror(errno));
|
||||
/* try until sigprocmask is not interrupted by a
|
||||
* signal */
|
||||
while (sigprocmask(SIG_BLOCK,&s_a.sa_mask,NULL)) ;
|
||||
|
||||
/* when blocked: */
|
||||
update_interfaces();
|
||||
}
|
||||
|
||||
/* a signal came in after we unblocked
|
||||
* or before we blocked? we may process one packet before
|
||||
* the list gets updated, but we do this check here never-
|
||||
* theless in order to not catch a signal during some
|
||||
* system call in update_interfaces
|
||||
*/
|
||||
if (update_interface_trigger) {
|
||||
update_interfaces();
|
||||
}
|
||||
|
||||
if (retval==-1) {
|
||||
if (errno==EINTR) {
|
||||
update_interfaces();
|
||||
} else if (errno) {
|
||||
syslog(LOG_WARNING,"select returned with %s",
|
||||
strerror(errno));
|
||||
}
|
||||
continue; /* fds are undefined -> no packets came
|
||||
in at this time */
|
||||
}
|
||||
|
||||
/* check all fds regardless of retval */
|
||||
for (i_s=select_set.i_s_list;i_s;i_s=i_s->next) {
|
||||
if (FD_ISSET(i_s->i_fd,&work_fd_set)) {
|
||||
process_packet(i_s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* cleanup... no. */
|
||||
}
|
||||
Reference in New Issue
Block a user