[[TOC]] This driver adds functionality to OMF so that you can use a Teltonika WiMAX USB device in an OMF script. The syntax to connect to a WiMAX channel in OMF is (for example, if the center frequency of the channel is 2.595GHz and the channel bandwidth is 10MHz): {{{ node.net.t0.channel = "2595000,10" }}} Note: this work assumes default settings on the Teltonika device (e.g. default telnet settings, IP settings, etc.) == Installing the driver == === Patches to omf-resctl (on the node) === In /usr/share/omf-resctl-5.3/omf-resctl/omf_agent/nodeAgent.rb: {{{ tel_count = 0 if (LSUSB) IO.popen("#{LSUSB} | grep 'Intel Corp. WiMAX Connection 2400m' | wc -l") {|u| wimax_count += u.gets.to_i } # Note: look for vendor:product rather than string to make sure usb-modeswitch works IO.popen("#{LSUSB} | grep '148e:099a' | wc -l") {|u| tel_count += u.gets.to_i } end if wimax_count > 0 require 'omf-resctl/omf_driver/wimaxcu' MObject.info "Found Intel WiMAX - using wimaxcu interface" AgentCommands::DEV_MAPPINGS['net/x0'] = WimaxcuDevice.new('net/x0', 'wmx0') AgentCommands::DEV_MAPPINGS['net/x1'] = WimaxcuDevice.new('net/x1', 'wmx1') end if tel_count > 0 require 'omf-resctl/omf_driver/teltonika' MObject.info "Found Teltonika WiMAX - using teltonika interface" AgentCommands::DEV_MAPPINGS['net/t0'] = TeltonikaDevice.new('net/t0', 'tel0') AgentCommands::DEV_MAPPINGS['net/t1'] = TeltonikaDevice.new('net/t1', 'tel1') end end }}} In /usr/share/omf-resctl-5.3/omf-resctl/omf_driver/teltonika.rb: {{{ # == Description # # This file defines the class TeltonikaDevice which is a sub-class of # WimaxcuDevice. # require 'omf-resctl/omf_driver/wimax' require 'net/telnet' require 'ipaddr' class TeltonikaDevice < WimaxDevice def unload # don't unload the kernel module #super() debug "Disconnecting WiMAX #{@deviceName}" dconnect = `#{@wget} --http-user admin --http-password admin -qO - "http://192.168.0.1/cgi/cli?stopSs"` clear = `#{@wget} --http-user admin --http-password admin -qO - "http://192.168.0.1/cgi/cli?clearScannerChannels"` if @deldef == false unroute = `#{@route} delete default gw 192.168.0.1` else unroute = `#{@route} del -net #{@routip} netmask #{@fnm} gw 192.168.0.1` end clearIP = deconfig @mode = @chnnl = @frequency = @bandwidth = @ip = @dip = @subnet = @nm = @fnm = @gw = @routip = @oldroutip = @oldnetmask = nil; @deldef = false $chflag = 0 end def deconfig host = Net::Telnet::new("Host" => "192.168.0.1", "Timeout" => 10, "Port" => 700, "Prompt" => /[$%#>] \z/n) host.login("admin", "admin01") { |c| print c } host.cmd("export interface=icc0"){ |c| print c } host.cmd("/etc/udhcpc.script deconfig"){ |c| print c } host.cmd("/bin/iptables -t nat -F") { |c| print c } host.close end def telnet_dynamicIP debug "Configuring iptables for dynamic IP" host = Net::Telnet::new("Host" => "192.168.0.1", "Timeout" => 10, "Port" => 700, "Prompt" => /[$%#>] \z/n) host.login("admin", "admin01") { |c| print c } begin str = host.cmd("ifconfig icc0") { |c| print c } #get the dynamic IP address: lines = str.split("\n") lines=lines.drop(2) lines=lines.reverse.drop(6).reverse vals=lines.collect { |row| column = row.split(" ") column[1] } addr=vals[0] addrAr=addr.split("\:") dynIP=addrAr[1] end while "#{dynIP}" <= "" host.close debug "Dynamic IP is: #{dynIP} \n" #add the rules to the iptables: rule1 = "/bin/iptables -t nat -A PREROUTING -p udp -d #{dynIP} -j DNAT --to 192.168.0.8" rule2 = "/bin/iptables -t nat -A PREROUTING -p tcp -d #{dynIP} -j DNAT --to 192.168.0.8" debug "Running #{rule1} \n" debug "Running #{rule2} \n" #second telnet session to add the rules: host = Net::Telnet::new("Host" => "192.168.0.1", "Timeout" => 10, "Port" => 700, "Prompt" => /[$%#>] \z/n) host.login("admin", "admin01") { |c| print c } begin host.cmd("#{rule1}") { |c| print c } host.cmd("#{rule2}") { |c| print c } rtrn = host.cmd("/bin/iptables -t nat -L") { |c| print c } end while !(rtrn.include? "192.168.0.8") host.close return dynIP end def telnetIfConf # If values are not set, use defaults ip = @ip || "0.0.0.0" netmask = @nm || "255.255.255.0" subnet = @subnet || 24 router = @gw || "" rip = IPAddr.new "#{ip}" @routip = rip.mask(subnet) deldef = @deldef if !deldef and ip != "0.0.0.0" tlres = "#{@route} del default gw 192.168.0.1; sleep 2; #{@route} add -net #{@routip} netmask #{netmask} gw 192.168.0.1" @deldef = true @oldroutip = @routip @oldnetmask = netmask end if netmask != "255.255.255.0" and deldef and ip != "0.0.0.0"#del route and add a new one if the netmask is different than the default tlres = "#{@route} del -net #{@oldroutip} netmask #{@oldnetmask} gw 192.168.0.1; sleep 2; #{@route} add -net #{@routip} netmask #{netmask} gw 192.168.0.1" end host = Net::Telnet::new("Host" => "192.168.0.1", "Timeout" => 10, "Port" => 700, "Prompt" => /[$%#>] \z/n) host.login("admin", "admin01") { |c| print c } host.cmd("ifconfig icc0 #{ip} netmask #{netmask}") { |c| print c } host.cmd("export ip=#{ip}"){ |c| print c } host.cmd("export subnet=#{subnet}"){ |c| print c } host.cmd("export interface=icc0"){ |c| print c } host.cmd("export router=#{router}"){ |c| print c } host.cmd("/etc/udhcpc.script bound"){ |c| print c } host.cmd("/bin/iptables -t nat -A PREROUTING -p udp -d #{ip} -j DNAT --to 192.168.0.8"){ |c| print c } host.cmd("/bin/iptables -t nat -A PREROUTING -p tcp -d #{ip} -j DNAT --to 192.168.0.8"){ |c| print c } host.close @fnm = netmask #final netmask. This will be used in 'unload' return tlres end def initialize(logicalName, deviceName) super(logicalName, deviceName) @mode = @ip = @dip = @subnet = @nm = @gw = nil @ifconfig = '/sbin/ifconfig' @wget = '/usr/bin/wget' @route = '/sbin/route' @deldef = false $chflag = 0 IO.popen("#{@ifconfig} #{deviceName} 192.168.0.8 netmask 255.255.255.0") end def buildCmd return nil if @mode.nil? cmd = nil case @mode when :channel return nil if @chnnl.nil? cmd = "#{@wget} --http-user admin --http-password admin -qO - 'http://192.168.0.1/cgi/cli?stopSs'; #{@wget} --http-user admin --http-password admin -qO - 'http://192.168.0.1/cgi/cli?addChannel frequency=#{@frequency} bandwidth=#{@bandwidth}'; #{@wget} --http-user admin --http-password admin -qO - 'http://192.168.0.1/cgi/cli?startSs'; #{@route} add default gw 192.168.0.1" $chflag = 1 when :ip return nil if @ip.nil? comRes = telnetIfConf cmd = comRes unless comRes.nil? when :netmask return nil if @nm.nil? comRes = telnetIfConf cmd = comRes unless comRes.nil? when :gway return nil if @gw.nil? comRes = telnetIfConf cmd = comRes unless comRes.nil? when :dynip return nil if @dip.nil? if $chflag == 1 comRes = telnet_dynamicIP else raise "CONNECT THE DEVICE (setup the channel first!) BEFORE ASSIGNING AN IP..." end cmd = nil else raise "Unknown mode '#{@mode}'. Should be 'channel', 'mtu', gway, dynip, netmask, or 'ip'" end return cmd end # # Return the specific command required to configure a given property of this # device. When a property does not exist for this device, check if it does # for its super-class. # # - prop = the property to configure # - value = the value to configure that property to # def getConfigCmd(prop, value) @propertyList[prop.to_sym] = value case prop when "ip" #ifconfig 192... etc.. @mode = :ip @ip = value return buildCmd when "netmask" #ifconfig 192... etc.. @mode = :netmask @nm = value @subnet = IPAddr.new("#{@nm}").to_i.to_s(2).count("1") return buildCmd when "dynip" @mode = :dynip @dip = value return buildCmd when "gway" @mode = :gway @gw = value return buildCmd when "channel" #assign a frequency and a bandwidth @mode = :channel @chnnl = value.split(/, */,2) @frequency = @chnnl[0] @bandwidth = @chnnl[1] return buildCmd end super end def get_property_value(prop) # Note: for now we are returning values set by a CONFIGURE command # when refactoring the device handling scheme, we may want to query # the system here to find out the real value of the property result = super(prop) #gets mac and ip addresses from ethernetDevice.rb result = @propertyList[prop] if !result return result end end }}} === Patches to omf-expctl === These changes should be made on the OMF server where the EC runs. (If you plan to run experiments in OMF Disconnected Mode, you should also patch the EC on the node). In /usr/share/omf-expctl-5.3/omf-expctl/node/nodeSetPath.rb: {{{ class NodeSetPath < MObject attr_reader :nodeSet, :path # List of valid 'PATHS' for a NodeSet VALID_PATHS_WITH_VALUES = { "mode=" => %r{net/[ew][01]}, "profile=" => %r{net/[x][01]}, "network=" => %r{net/[x][01]}, "type=" => %r{net/[ew][01]}, "rts=" => %r{net/[ew][01]}, "rate=" => %r{net/[ew][01]}, "essid=" => %r{net/[ew][01]}, "ip=" => %r{net/[ewxt][01]}, "dynip=" => %r{net/[t][01]}, "channel=" => %r{net/[ewt][01]}, "tx_power=" => %r{net/[ew][01]}, "netmask=" => %r{net/[ewxt][01]}, "mac=" => %r{net/[ewx][01]}, "mtu=" => %r{net/[ewxt][01]}, "arp=" => %r{net/[ewx][01]}, "enforce_link=" => %r{net/[ewx][01]}, "route" => %r{net/[ewxt][01]}, "gway" => %r{net/[t][01]}, "filter" => %r{net/[ewx][01]}, "net" => // } VALID_PATHS_WITHOUT_VALUES = { "down" => %r{net/[ewxt][01]}, "up" => %r{net/[ewxt][01]}, } VALID_PATHS = VALID_PATHS_WITH_VALUES.merge(VALID_PATHS_WITHOUT_VALUES) VALID_PATHS_RE = { /[ewxt][01]/ => /net/ } }}} === Other required configuration on node === Install usb-modeswitch-data and usb-modeswitch (need recent version, >=1.1.9, such as the one provided for oneiric) * [http://packages.ubuntu.com/oneiric/i386/usb-modeswitch-data/download] * [http://packages.ubuntu.com/oneiric/i386/usb-modeswitch/download] In /etc/udev/rules.d/70-persistent-net.rules, add the following (so that teltonika device will get the name tel0): {{{ KERNEL=="eth*", DRIVERS=="cdc_ether", NAME="tel0" }}} If /etc/udev/rules.d/75-persistent-net-generator.rules exists, remove it so that your /etc/udev/rules.d/70-persistent-net.rules will not be overwritten. Make sure you have all the other rules you need (e.g. for Ethernet control interface) in /etc/udev/rules.d/70-persistent-net.rules because new ones will not be generated anymore. == Usage: Sample Scripts == === With Static Addressing === {{{ defProperty('runtime',20,"Time in second for the experiment is to run") defProperty('client',"192.168.10.1","IP address of iperf server") defProperty('interval', 1, "Interval of Iperf measurements") defProperty('sender', 'omf.witest.node10', "ID of sender node") defProperty('sender', 'omf.witest.node1', "ID of sender node") defGroup('Group', property.sender) do |node| node.net.t0.channel = "2595000,10" node.net.t0.mtu=1470 node.net.t0.ip = "192.168.10.110" node.net.t0.netmask = "255.255.255.0" node.addApplication("test:app:iperf") do |app| app.setProperty('client', property.client) app.setProperty('interval', property.interval) app.setProperty('time', property.runtime) app.measure('TCP_Info', :samples =>1) end end defGroup('Group', property.receiver) do |node| node.net.x0.profile = '51' node.net.x0.ip = property.client node.net.x0.netmask = "255.255.255.0" node.addApplication("test:app:iperf") do |app| app.setProperty('server', true) app.setProperty('interval', property.interval) app.measure('TCP_Info', :samples =>1) end end onEvent(:ALL_UP_AND_INSTALLED) do |event| wait 50 info "This is an iperf experiment using a teltonika modem" allGroups.startApplications wait property.runtime wait 2 allGroups.stopApplications wait 2 Experiment.done end }}}