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:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions
+20
View File
@@ -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
+465
View File
@@ -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();
+125
View File
@@ -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
+586
View File
@@ -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. */
}