Simulating host unreachable - how to achieve/implement it - perl

Here is my scenario:
A is a provisioning server and B is an client. Whenever there is any change in B's setup, it uploads the appropriate config file to A.
I am working as an automation engineer to automate it. One of the scenario says to disconnect A from network or stop the server A. perform some changes to B and make sure that B failed to upload the files to provisioning server A.
To automate it, the simple way to stop the server A and do the appropriate actions.
Since A and B are also used for other purposes by other parties so I can not either disconnect A or B from network OR stop the server at A.
So, I am looking forward for any solution so that I can simulate the host (provisioning server) unreachable scenario. So when B will send an update to A it will fail but in actual A is running as usual.
Please suggest me some way to achieve it.
I am using Perl as a programming language but I am fine if solution is available in other language.

I've done this before using a null route. This is something that best done from the shell with the ip command.
# blackhole all packets destined for 192.168.2.1
ip route add blackhole 192.168.2.1
# to delete the same route, replace add with del
ip route del blackhole 192.168.2.1
Depending on your use case, an unreachable route may work better, as it returns ICMP-unreachable instead of discarding the packets, although they tend to have the same effect.
ip route add unreachable 192.168.2.1
And for thoroughness, if you really wanted to simulate a host-unreachable situation (vs a network-unreachable), you would have to do that at the firewall level.
# resond with icmp-host-unreachable for *any* outbound packet to 192.168.2.1
iptables -I OUTPUT -d 192.168.2.1 -j REJECT --reject-with=icmp-host-unreachable
# delete the same rule (without looking up the rule #)
iptables -D OUTPUT -d 192.168.2.1 -j REJECT --reject-with=icmp-host-unreachable

Another, perhaps easier option is to change the configuration on B to have a bogus IP address for A (e.g. 192.0.2.0) when performing the test.

Test::MockObject::Extends - great for modifying small parts of modules to create specific testing scenarios. Works great for things that you can't test well because they affect things in production or in places that you don't control.
#!/usr/bin/perl
use strict;
use warnings;
use Test::MockObject::Extends;
#Fake module that has your remote connect subroutine
use Fake::Module;
my $object = Fake::Module->new();
#replace your obj with a copy that Test::MO:E will let us mess with
$object = Test::MockObject::Extends->new( $object )
#replace your connect function with a temp fake version
$object->mock(
'your_remote_connect_sub' => sub {
#Whatever data that should returned by your connect function if the server is unavailable
return undef;
},
);
#test your sub now
if ( !defined( $object->your_remove_connect_sub() ) ) {
print "Remote server unavailable\n";
}

Related

How do 2 devices communicate over an ethernet switch

Before I proceed, I'd like to mention that I did try to research this topic on the internet, but I still need clarification.
Let's say I have two Linux machines connected to a switch (and only to a switch). Machine A has an IP address of 10.0.0.1 and machine B -- 10.0.0.2. I used nmcli command to set the IP address and create an ethernet interface for each machine. Everything works as expected.
Now, the confusing part is how machine A can find machine B and vice versa? I'm using the following command to connect from machine A to machine B:
ssh userB#10.0.0.2
And it works, even if this is the very first data transmission. This surely means that machine A somehow already knew the machine's B MAC address; otherwise, the frame wouldn't find its way to machine B. But how? Since the IP address is meaningless to the switch (Level2), why when I do ping 10.0.0.2 or ssh 10.0.0.2, it still works?
Probably the ARP cache was already populated. Maybe there was a grations ARP broadcast:
Every time an IP interface or link goes up, the driver for that interface will typically send a gratuitous ARP to preload the ARP tables of all other local hosts.
If not, most likely an ARP request/reply was happening right before the first ping. Check the arp command or ip neigh.
In general I suggest you use Wireshark to explore what's going on, or something like tcpdump -n -i eth0 not ssh if your are working remotely (note the -n to prevent name resolution). You can also record traffic with tcpdump -s 9999 -w output.pcap and view it later in Wireshark.
If you sniff network traffic on a third PC, keep in mind that switches will not send traffic to all ports when they have learned where the destination is. Some switches allow you to configure a mirror port to observe all traffic to or from a certain port. Either way you should always be able to observe ARP requests as they are broadcast.
basically, when the first packet reach to the switch ( virtual or physical switch ), the switch will populate arp broadcast packet for the sake of getting all devices mac and ip addresses. so even though ip addresses seem meaningless to switches ( cause they're layer 3 concept but switch is for layer 2 ), switches still need those data to process the packets. because this is how we, as human beings, interact with computers for transmitting data by using ip addresses.
when you ping a device, like 10.0.0.2, the switch will search in it's arp table and find the corresponding mac address and also the interface for reaching to the destination.
the best way to comprehend the whole process is to capture the data using wireshark or even implementing a simple topology in softwares like cisco packet tracer.

Server Connection with public IP

I have setup a simple HTTP java server running locally on port 8000. It simply prints a message "Hello world" when a request comes. When I try to ping it from the browser by running http://localhost:8000/test I get my message printed.
I want to get the same results from another computer that is not local. When I try to use my public IP lets say http:/43.xxx.xxx.xxx:8000/test (even from the same machine) I get an ERR_CONNECTION_REFUSED .
I probably suspect that has something to do with the firewall. Can anyone guide me a little more because I lack the experience?
Thanks in advance
You don't specify what host OS your server/firewall is running so I'll keep this generic...
Without knowing your application, it seems like the server is sending a reset (RST packet) when the first SYN packet shows up indicating that the port (on that interface [your external]) is closed. You can do a quick port scan from here (https://mxtoolbox.com/PortScan.aspx) if you don't have access to a remote machine to test with. Odds are, TCP/8000 will not be open.
If it is, in fact, closed, you'll have to look at the firewall that your host OS is running and find out how to allow TCP/8000 to your host. In a major firewall vendor, your rule would look similar to this:
Source: Any
Destination: Your Public IP Address
Service: TCP/8000
Action: Allow
Logging: Full
That being said, you mentioned this was a PC so look into "iptables" (if you're running *nix) or the Windows Firewall (if you're running Windows) on adding firewall rules (Unfortunately I just joined and can't ask questions/comments, yet).
If you really want to find out what packet is being sent, run a tcpdump on your external interface (let's say eth1) (assuming your remote IP is 1.2.3.4 and your home public IP is 4.5.6.7):
tcpdump -nn -vvv -e -s 0 -X -c 100 -i eth1 host 1.2.3.4 and host
4.5.6.7 and port 8000
Here you're looking for the SYN/SYN-ACK/ACK for a successful TCP negotiation or SYN/RST if there is a firewall rejecting (not dropping) the TCP stream to the port.
Once the port is open on the host OS firewall, take a look at the application to make sure it's configured properly. If this were a standard webserver, you could take a look at the configuration files for the "Allow from" directives to make sure that everyone can access the site. If this is a custom application that you've created, you'll have to check this yourself.
I finally solved my problem. I needed to open a forwarding port in my router that maps my local ip address to the public. My router is TP Link so this what I did:
http://www.tp-link.com/us/faq-72.html
Also in order for this to work every time and not to have to reconfigure this every time I reconected to the router (because I get a new local IP), I have created a static local ip for my server following this guide:
http://www.tp-link.com/us/faq-182.html
Thanks for all the replies.

Block facebook.com using openwrt router

I am using OpenWRT router. I need to block a URL or multiple URLs (Not IP) for specific time. for example, I want to block facebook.com so that clients of this router cant access the website. firewall rules should have the option to do that but I dont know how to do that.
Here is one way to block by domain name rather than by IP address.
The main reason of why you need such a complicated method is that each domain name (e.g. facebook.com) may be resolved as different IP address at any given time. So, we need to keep a list of resolved IP addresses and add iptables rules based on this list.
First, you should enable logging in dnsmasq config:
uci set dhcp.#dnsmasq[0].logqueries=1
uci commit dhcp
/etc/init.d/dnsmasq restart
This will give you log entries like:
daemon.info dnsmasq[2066]: reply facebook.com is 31.13.72.36
Now, you just have to constantly parse syslog and add corresponding iptables rules like this (note that you most likely need a more versatile script and ipset for better performance):
logread -f | awk '/facebook.com is .*/{print $11}' | while read IP; do iptables -I OUTPUT -d $IP -j DROP; done

how to check whether perl cgi request originates from localhost

I would like to expose a service written in Perl to localhost HTTP requests. I do not want to modify Apache configuration. How to check whether a Perl CGI HTTP request originates from localhost?
I want for this check to succeed even if this call is made through a virtual host eg. https://www.myserivce.com/hidden/service.pl given that the call is made from inside of www.myserivce.com.
REMOTE_ADDR, but that's a dumb way to do it because you put the authentication logic in the application.
Instead, bind a stand-alone Web server to local interface only, thus the operating system's IP/networking stack guarantees that no request from outside can reach the server.
I think that if you put in /etc/hosts file an entry with myservice.com and ip 127.0.0.1 then all the requests from localhost to your site will have the REMOTE_ADDR set to 127.0.0.1 .
I am afraid that this is the only way to do it, unless you are making requests to 127.0.0.1/hidden/service.pl instead of myservice.com/hidden/service.pl
I have used the following code:
my $server_addr = inet_ntoa(scalar gethostbyname(hostname() || 'localhost'));
my $call_addr = $query->remote_addr();
die unless $call_addr eq "127.0.0.1" || $call_addr eq $server_addr;
I do not think it covers all cases, but seems to work with my setup. If anybody knows a generic solution then please submit it here.

How can I determine the local machine's IP addresses from Perl?

Is there a clean and OS independent way to determine the local machine's IP addresses from Perl?
So far I have found the following solutions:
parse the output of ifconfig and ipconfig (hard, different windows versions have different ipconfig outputs)
establish a network connection to a well-known IP and examine the socket's local IP address (won't work if I can't establish a connection and will determine only one IP address)
Any better suggestion?
Net::Address::IP::Local looks promising.
use Net::Address::IP::Local;
# Get the local system's IP address that is "en route" to "the internet":
my $address = Net::Address::IP::Local->public;
You also have some other options, including your solution to "establish a network connection to a well-known IP and examine the socket's local IP address".
In that case (establishing network connection) however, that article points out that:
there is no such thing as a host's IP address.
Network interfaces have IP addresses, not hosts, and a single network interface can have many (virtual) IP addresses. The operating system's routing subsystem decides which network interface and IP address to use to connect to a remote machine.
If your machine only has one external network interface, and this interface only has one IP address then this IP address is commonly called the machine's address, but that is inaccurate.
For example, if the machine is connected to a VPN via a virtual interface it will use this interface's IP address to connect to another machine on the VPN, not the external IP address
Amongst the other solutions: Sys::Hostname - works if it comes up with a resolvable hostname.
use Sys::Hostname;
use Socket;
my $addr = inet_ntoa((gethostbyname(hostname))[4]);
print "$addr\n";
In my case, I need a solution without any non-core dependencies. I came up with this after studying the code in Net::Address::IP::Local:
#!/usr/bin/env perl
use strict;
use warnings;
use IO::Socket::INET;
my $local_ip_address = get_local_ip_address();
print "$local_ip_address\n";
# This idea was stolen from Net::Address::IP::Local::connected_to()
sub get_local_ip_address {
my $socket = IO::Socket::INET->new(
Proto => 'udp',
PeerAddr => '198.41.0.4', # a.root-servers.net
PeerPort => '53', # DNS
);
# A side-effect of making a socket connection is that our IP address
# is available from the 'sockhost' method
my $local_ip_address = $socket->sockhost;
return $local_ip_address;
}
get_local_ip_address() should return the same string as Net::Address::IP::Local->public_ipv4.
If desired, you can change the PeerAddr attribute (in the arguments to the constructor for IO::Socket::INET) to a local DNS server.
To retrieve the IP address of all interfaces, use IO::Interface::Simple:
perl -MIO::Interface::Simple '-Esay $_->address for grep { $_->is_running && defined $_->address } IO::Interface::Simple->interfaces'
If you are not interested in 127.0.0.1 (loopback) you can filter on $_->is_loopback.
Perldoc has an answer to this question in its FAQ ("perlfaq9") - using different modules (which are parts of the Standard Library) or even a built-in function.
I've had good success with IO::Interface on Linux and Solaris, and I think it even worked on AIX but I can't recall for sure. Poking around on search.cpan.org, rt.cpan.org and ActiveState's various sites, it looks like IO::Interface may be experiencing build problems on Windows. I guess the only way to know if it's available is to search for io-interface in PPM.
use WMI?
Example of extracting IP addresses (in Powershell, but it's pretty clear what's happening)
Example of accessing WMI from Perl (not the same WMI functions, but again the process is reasonably clear)
EDIT: after a search on Google codesearch for Networkadapterconfiguration and language "perl":
Example looks like pretty much what you need
EDIT2: In fact the OCS code seems to contain code for most platforms to do this, so while there may be no one set of code that does this, you may be able to re-use their ideas. It's GPL'd, though.
For example, here's the Solaris code. Other bits cover BSD, Linux, MacOS...
getting a network interface's IP address in Perl without additional modules usage and 'ifconfig' output parsing
Net::Address::IP::Local works fine, but since the original poster asks for all the local addresses, I think this one is better:
http://www.perlmonks.org/?node_id=166951
It worked fine for me with ActivePerl for Windows XP.
for windows I use
foreach (split(/\r?\n/,`netstat -r`))
{
next unless /^\s+0.0.0.0/;
#S = split(/\s+/);
# $S[3] = Default Gateway
# $S[4] = Main IP
}
The first line starting 0.0.0.0 is the default gateway. There maybe multiple gateways.
Lines starting 255.255.255.255 are also useful. netstat -r and route print are the same.
Can be adapted for OSX, Linux not so helpful.
I have used a combination of these Linux commands so no dependancy on any perl module.
hostname -i
hostname -I
ls /sys/class/net
ip -f inet addr show eth0| grep -Po 'inet \K[\d.]+'