Getting public ip from home network

I’m using ssh on my raspberry pi, but to access it when I’m away from my home network I’ve got to have a public ip. My ISP doesn’t provide static ip for private users, so it may change whenever my ISP feels the need to do so. This of course means that I might not be able to access my raspberry pi over ssh if my ISP changes my ip address.

First of all, what is my public ip? You can find it at this address: http://www.whatsmyip.org/. Ok, but we obviously can’t check the IP from our home network when we’re not at home, right? So let’s automate it with python!

First of all, the link above gives a lot of information, but for this script we only need the ip. We have another address that provides just that: http://ip.42.pl/raw. As you can see, it only states the ip, nothing else.

To read the ip from a webpage we need to import urlopen from urllib2 like this.
from urllib2 import urlopen.
This enables us to do this:
public_ip = urlopen('http://ip.42.pl/raw').read()
the public_ip variable now contains our most recent public ip. Great!

What we’re interested in is if it has changed since the last time this script was called. To check that we need to store the ip somewhere. Since we’ll only store the last public ip, it is sufficient with a text-file for this. If you were to store historical data, like all ip addresses assigned to your network by your ISP and when it was changed I would suggest you use a database like mongodb.

Back to our script, we want to store the ip in a file called “publicip.txt”. On linux, the file ending is not necessary, but I like to include them for readability.
filename = 'publicip.txt'
We also need to import os with:
import os
This is because when we’re running the script, we want to check if the file already exists.

if os.path.isfile(filename):
  file = open(filename,'r+')
  old_ip = file.read()
  file.close()
else:
  file = open(filename,'w')
  file.write(public_ip)
  file.close()


What this snippet does is that it checks if the file exists. If it does, it opens it and tries to read it to get the stored ip and saves it in memory as the variable old_ip. If the file doesn’t exist it opens the file to write to it (this also creates the file). It then writes the ip we got from the website to the file and closes the file.

Ok so far, but we still have to make logic for if the file exist and contains an ip different from the one we got from the website.

if public_ip != old_ip:
  file = open(filename, 'w')
  file.write(public_ip)
  file.close()

This snippet basically says that if the public_ip is different from the old_ip, we open the file to write to it (the ‘w’ parameter truncates the file, meaning that the file is cleared of all contents. If you want to append, use ‘a’). We then write the new ip and close the file.

Awesome! But we also want to receive some sort of message that contains the new ip, right? Let’s use mail.

First, we need to import some libraries:
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
import smtplib

Then we need to open a connection to a smtpserver. We’ll use gmail for this script.
server = smtplib.SMTP('smtp.gmail.com', 587)
server.ehlo()
server.starttls()
server.ehlo()

Next, we’ll provide some mail parameters:
username = 'themevik89@gmail.com'
password = 'somethingsupersecretaboutsocks'
fromaddr = 'themevik89@gmail.com'
toaddr = 'themevik89@gmail.com'
subject = 'Public ip changed!'

Username and password should be somewhat self explanatory. the fromaddr and toaddr are addresses, in this case from me to me. Subject is going to be used for the subject-line in the mail. Using the gmail smtp server might require you to do some additional steps when creating this script, namely creating what is known as a app-password. You can find information about it here: https://support.google.com/accounts/answer/185833. It is a password for your app that doesn’t need 2 way authentication or captchas. It is perfect for this kind of automation scripts, but might provide a security risk (since it is the only authentication needed to access your google account).

The next part is creating the mail, parsing it to text and sending it like this:
msg = MIMEMultipart()
msg['From'] = fromaddr
msg['To'] = toaddr
msg['Subject'] = subject
body = 'Your public ip has changed and is now: '+public_ip
msg.attach(MIMEText(body,'plain'))
server.login(username, password)
text = msg.as_string()
server.sendmail(fromaddr, toaddr, text)

Here we construct a msg-object of the type MIMEMultipart. It contains values for sender (‘From’), receiver (‘To’) and subject (‘Subject’). We also create a body containing a short message and the new public_ip, convert it to MIMEText and attach it to the message. Then we convert the msg from MIMEMultipart to string and send it using the server-object we created earlier.

You can see the whole script on my GitHub-page: https://github.com/TM89/public-ip-finder.

When the script is called and the ip has changed, I receive an email looking like this.

Success! Public ip changed and message received from script

Email received from script

To run it on timed intervals, check out this post: Running script using crontab on Ubuntu

And that’s that! Let me know what you think in the comment section below!

How to install Raspbian using Ubuntu Terminal

20150627_021454

My Raspberry Pi died again, probably because I’m using the wrong kind of SD-card. My friend Marius (his website) suggested that HD-card class 4 may have issues when running for a long time, like when it is the drive for a Raspberry Pi, and that I should replace it with a class 10 SD-card.

Anyways…

For this project I’m using my old Raspberry Pi Model B.
Start by downloading the image-file from . I’m using the wheezy raspbian .zip file for offline install. I actually have the .zip from before since I’ve done this before and I’m going to use it. Just replace the version number with the correct version for your download for the commands that are about to follow.

Open a terminal with Ctrl+alt+T (if you’re using Unity on Ubuntu). Navigate to the folder containing the recently downloaded .zip file.

Unzip with: unzip ~/2014-09-09-wheezy-raspbian.zip

You should now have a file named 2014-09-09-wheezy-raspbian.img or similar if your downloaded file is older or more recent than mine.

Now, check which devices are currently mounted using: df -h
If you run this first without your SD-card in the slot, and then with the SD-card in the slot, you should be able to figure out which device is the recently mounted SD-card. For me it’s /dev/sdb1. The number in the end of the SD-card path tells which partition is mounted on the SD-card, but in this case we want to write to the whole SD-card.

To do that we first need to unmount the card with umount /dev/sdb1. If you run the df -h again you won’t see the SD-card (at least you should not).

Now to the part where we actually write stuff to the SD-card!

We will use dd bs=4M if=2014-09-09-wheezy-raspbian.img | pv | dd of=/dev/sdb
bs stands for blocksize. It should work with 4M as blocksize, but if it doesn’t you should try reducing the blocksize to 1M. It will take longer time writing the imagefile to the SD-card, but if it works it’s worth it. Remember to use sudo if you get ‘permission denied’.

When it’s done you plug your SD-card into the Raspberry Pi, power it on and connect it to your network.
I’m not going to use a monitor to verify that it is running. Instead, I will attempt to access it with ssh. To do that I need to know what its IP is. I will use the same logic as I did with the mounted drives.

for this part I assume you are on a network that doesn’t prohibit portscanning and that you have NMAP installed. If not, you can install it with:
sudo apt-get install nmap

First, we need to figure out which subnet we’re on by using:
ifconfig

This command will list information about all network interfaces. For me, as I’m currently using wireless for my internet connection, I’m interested in the wlan0 interface. One of the parameters is called inet addr: 192.168.2.11 and this tells me I’m on a local network (192.168.x.x), on subnet 2 (x.x.2.x), and that my laptop has been assigned the id 11 (x.x.x.11) on this network. That means that when I connect my Raspberry Pi to my router with a cable it will get an adress on the same network (192.168.2.x), but I don’t know what the last part of the IP will be.

I will find this out by listing all active hosts on this network using nmap:
nmap 192.168.2.1/24

This revealed that 5 hosts: 1 (my router), 2, 3, 5 and 11 is currently up and active on my network. Now I connect my Raspberry Pi to the router and power and run the command again.

It doesn’t show up… Allright, now we need to find out why. I start by plugging my HDMI-cable into the Raspberry Pi and my tv and select the right channel. Next, I unplug the Raspberry and plug it back in again to reboot it. The data on the SD-card is corrupt…

I plug the SD-card back into my computer, use df -h to find it and then sudo mkfs /dev/sdb1 to format it. Make sure you format the right device, just saying…

I don’t know why, but it seems that the command I used to write the imagefile to the SD-card didn’t work as intended. When I used sudo dd bs=1M if=wheezy.img of=/dev/sdb instead it worked.

Booting the Raspberry with the freshly rewritten SD-card works fine, and by using nmap again I find my Raspberry’s IP.

Since the Raspberry is running stock software the ssh login credentials is
ssh pi@192.168.2.4
password: raspberry

I get a connection and I’m now able to control the Raspberry from my laptop!

Success!

Success!

I hope this post may help you when trying to make your Raspberry boot from SD-card, but if you want more in depth explanations and alternate ways to accomplish this you should check out this guide. Let me know what you think in the comment section below!