LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   [SOLVED] Detect if input is a valid ip & subnet (https://www.linuxquestions.org/questions/programming-9/%5Bsolved%5D-detect-if-input-is-a-valid-ip-and-subnet-4175610005/)

pedropt 07-17-2017 03:33 AM

[SOLVED] Detect if input is a valid ip & subnet
 
I am doing a script that requires the user to insert an ip , in case user writes text then it will abort the script .

I want 2 options to be available .

1st - user can enter a single ip (ex: 100.0.0.0)
2nd - user can enter a single ip & subnet (ex: 100.0.0.0/24)

the first part i was able to figure out with this code :

Quote:

echo "Enter Ip range to scan ex: 192.168.1.1/24"
echo "IP Range : ";tput sgr0
read ip
if [[ $ip =~ ^[0-255]+\.[0-255]+\.[0-255]+\.[0-254]+$ ]]
then
echo "$ip is valid"
else
echo "$ip is not valid"
exit 1
fi

Now , how to add to this if code :
[[ $ip =~ ^[0-255]+\.[0-255]+\.[0-255]+\.[0-254]+$ ]]

the possibility of a subnet input between 0 & 32 (ex: 100.0.0.0/24)?

I only need the if instruction so i can write an "elif" instruction if the first code .

business_kid 07-17-2017 04:03 AM

What exactly are you trying to check on the subnet? That a subnet exists?

I'm thinking of my router. The internet sees a single address in the 84.xxx.xxx.xxx range; I see an address 192.168.178.1 for the router, and pretty random addresses in the 192.168.178.xxx range for the various attached devices (this pc, printer, poser phone, tablets, etc.). 84.xxx.xxx.xxx passes trasffic to and from 192.168.178.xxx but never the twain shall meet. It's a tall order to ferret out the 192.168.178.xxx at all, let alone with a few lines of bash scripting.

Have you played with nmap? Read the man page and have a go. It's probably a better way.

pedropt 07-17-2017 05:17 AM

Quote:

What exactly are you trying to check on the subnet? That a subnet exists?
No , i am trying to build an is statement to check if user input is between these values :
0.0.0.0/0 - 255.255.255.254/32

Quote:

Have you played with nmap? Read the man page and have a go. It's probably a better way.
No ...comments

michaelk 07-17-2017 06:32 AM

Code:

if [[ $ip =~ ^[0-255]+\.[0-255]+\.[0-255]+\.[0-254]+$ ]]
A regex expression matches a string of characters and not a range of numbers. Your expression does not work for 192.168.0.1.


Code:

if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]
while this works for any digit you still need to verify that the range is 0-255 for each octet. You can add another regex to check for the netmask mask but again you need to verify the range is correct.

Code:

if [[ $ip =~ /[0-9]{1,2}$ ]]; then
  echo "netmask valid"
else
  echo "netmask not present or invalid"
fi


pedropt 07-17-2017 06:42 AM

Thanks Michaelk , it is working perfectly .

I will leave here the final code for others :

Quote:

if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]
then
if [[ $ip =~ /[0-9]{1,2}$ ]]; then
echo "netmask valid"
else
echo "netmask not present or invalid"
fi
else
echo "$ip is not valid"
exit 1
fi
However , how to determine when the netmask is invalid or not present in the code ?

One idea was to put the first check of netmask to see if leters was inputed by user in that field .

Quote:

if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]
then
if [[ $ip =~ /[a-z]{1,2}$ ]]; then
echo "netmask ok without letters"
else
echo "netmask not ok with letters"
exit 1
fi
if [[ $ip =~ /[0-9]{1,2}$ ]]; then
echo "netmask ok with numbers"
else
echo "netmask not present"
fi
else
echo "$ip is not valid !"
exit 1
fi
Other idea would be defining the ip variable correctly after if statement check it .

In case if detects :
$ip = 192.168.1.1/ewwer

then
$ip = 192.168.1.1

but if :
$ip = 192.168.1.1/24
then it is ok and do not change it .

michaelk 07-17-2017 06:57 AM

Code:

if [[ "$ip" =~ (([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])) ]]; then
I have not verified but this regex expression should verify all combinations of octets without additional code. You can use a subtring functions to extract anything from a / to the end of string and go from there.

pedropt 07-17-2017 07:25 AM

Quote:

if [[ "$ip" =~ (([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])\.([01]{,1}[0-9]{1,2}|2[0-4][0-9]|25[0-5])) ]]
then
echo "ip ok"
else
echo "ip not ok"
is not working correctly

192.168.0.1/345 = ok
192.168.0.1/tuy = ok

I believe that expression is only to check the ip address .

syg00 07-17-2017 07:29 AM

I would be monumentally disappointed - to the point of disbelief - if CPAN didn't have something to handle this trivially. Including the subnet check.
Why (re-)invent a square wheel ?.

Turbocapitalist 07-17-2017 07:44 AM

Yes, there are several modules available in CPAN. It's trivial to pound out a script to check addresses or networks. I'd hate to re-implement the whole module in the shell:

Code:

#!/usr/bin/perl -T

use Net::IP;
use strict;
use warnings;

my $ip = shift
    or die ("Missing address or network.\n");

$ip =~ m/^([\/\:\.0-9a-fA-F]+)$/
    or die("Bad address or network format.\n");

my $addr = new Net::IP($ip)
    or die (Net::IP::Error());

exit ( 0 );

That returns non-zero if the network is invalid and returns zero if it is valid.

Additionally, if there is something wrong with the address or network then an error describing the problem goes to stderr.

pedropt 07-17-2017 08:55 AM

My whole script is written in bash , to adapt it to perl would take me a course of perl , which i don't want to get in now .

This issue is "easy" if we understand the logic that must take .
Many of the impossible bash code that we think that is not possible , is because we are unable to figure out a logic way to do it .

the target is an ip and subnet (192.168.1.1/24)

logic to be acquired in code :

0 - check if there is a slash in the var (/) , case "/" does not exist then go to step 5
1 - split the var in / and check if it is a number or letters in the subnet .
2 - case it is letters then it is invalid
2a- set the current var with user input until the "/"
ex user input : 100.0.0.0/sfg

2a if statement = change original var from 100.0.0.0/sfg to 100.0.0.0
3 - case it is numbers then it must between 0 and 32
4 - check the var until the "/"
5 - it is an ip ? then it must be between 0.0.0.0 and 255.255.255.255
6 - if it not an ip then the value is not accepted .

The big problem is to make all these steps with if statements and regex .

My experience with "if statements" is very limited , this is why i post it here , because many people here programming is their everyday job .

syg00 07-17-2017 10:22 PM

I'd be pretty comfortable stating all the current respondents would be more than adept at bash. And can recognise it limitations.
There is no need to re-write all your script, just the IP testing bit. Turbocapitalist has given you a more than adequate solution that can be simply called and the return code tested, just like any other command you would use. You would likely have to install Net::IP from CPAN - search the web for instructions. Might even be in your repos, it is for Fedora.

pedropt 07-18-2017 01:56 AM

eventually i will figure out a way to do it , it would take 50 lines or more of code , but i will make it .

Thanks all for your help .

pedropt 07-18-2017 06:03 AM

Like i told in the previous post , i would figure out a way to do it in bash , and i did it .
However the regex michael give allows to input an ip like 999.999.999.999 , and that cannot happen .
But the rest of the code is working as it should be .

Quote:

#!/bin/bash
echo -ne "Enter IP or IP range : ";tput sgr0
read ips
echo $ips > tmp
ip="$ips"
if [[ $ip =~ "/" ]]
then
chk="/"
chk1=`grep -o $chk <<< "$ip" | wc -l`
if [ $chk1 != 1 ]
then
echo "Invalid IP"
exit 1
else
sub=`cat tmp | cut -d "/" -f 2`
if [[ ! $sub =~ ^[^A-Za-z]+$ ]]; then
echo "Invalid IP"
exit 1
else
if [ $sub -lt "0" ]
then
echo "Invalid IP"
elif [ $sub -gt "32" ]
then
echo "Invalid IP"
exit 1
else
ipo=`cat tmp | cut -f1 -d "/"`
if [[ $ipo =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]
then
ip=`cat tmp`
echo "Valid $ip"
rm -f tmp
else
echo "Invalid IP"
fi
fi
fi
fi
else
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]
then
echo "Valid IP"
else
echo "Invalid IP"
fi
fi
code still needs some refining that i did not had time yet to do it .

Turbocapitalist 07-18-2017 06:19 AM

For IPv4 addresses, you'd just have to look at each number and then see if it is in the range 0-254. It might be a good idea use a function for the actual comparison to avoid coding the same sequence four times.

For temp files, consider using mktemp

Code:

tmp=$(mktemp pedropt.ip.XXXXXXX);
...
test -f $tmp && rm -f $tmp;

And instead of backticks ` ` you might consider using $( ) as it is a bit more modern and readable.

pedropt 07-18-2017 06:41 AM

yup . i know , i have to split the ip in 4 parts on the dots and check in their values are between 0 and 255 .
But that i will do it other time .


All times are GMT -5. The time now is 02:50 AM.