Εισαγωγή
Introduction
Πολλές φορές χρειαζόμαστε να στήσουμε κάποιου είδους server έτσι ώστε να μπορούμε να έχουμε πρόσβαση σε αυτόν από τον "έξω κόσμο", δηλαδή από παντού, όπου υπάρχει πρόσβαση στο διαδίκτυο. Το πρόβλημα σε αυτό είναι πως οι περισσότεροι πάροχοι Internet (Internet Service Providers, εν συντομία ISPs) δε δίνουν σταθερή διεύθυνση IP στα router που έχουμε σπίτι μας (εκτός, φυσικά, αν το ζητήσουμε με κάποια παραπάνω χρέωση). Έτσι, θα πρέπει κάθε φορά να γνωρίζουμε την IP διεύθυνση του router μας για να μπορέσουμε να έχουμε πρόσβαση στον server που κάθεται πίσω από αυτό. Αυτόν ακριβώς το σκοπό έχει και το παρόν άρθρο. Τι θα λέγατε όμως, αν το σύστημα που θα μας βοηθούσε είναι ένα Raspberry Pi; Όχι πως αυτά που περιγράφονται εδώ δε μπορούν να εφαρμοστούν και σε άλλο σύστημα υπολογιστή... Αντιθέτως, ένα δυνατό σημείο του ανοικτού λογισμικού, είναι πως η διαχείρισή του γίνεται με τον ίδιο ακριβώς τρόπο, είτε αυτό τρέχει σε ένα μηχάνημα με 64 επεξεργαστές, είτε τρέχει σε μια... τοστιέρα (βλ. NetBSD!).
Τι θα δούμε
What is in this article
Παρότι το άρθρο προορίζεται σε ανθρώπους που ξέρουν να ρυθμίζουν routers, να στήνουν servers, να διαχειρίζονται υπηρεσίες σε λειτουργικά όπως το Linux και να έχουν τη δυνατότητα, ίσως, να ασχοληθούν με ένα gadget του τύπου Raspberry Pi, εν τούτης θα δοθούν πληροφορίες ακόμα και σε πιο "αρχικά" πράγματα. Μέσα σε όλους τους σκοπούς του άρθρου είναι να μπορέσει ακόμα και κάποιος αρχάριος να βρει κάποιες εναρκτήριες πληροφορίες, εισαγωγικές περισσότερο σε όλα αυτά με τα οποία σχετίζεται η πρόσβαση ενός υπολογιστή σε ένα άλλο, μέσω του διαδικτύου. Ακόμα και για την πληρότητα του άρθρου, δε θα μπορούσαν να λείπουν αυτές οι πληροφορίες. Αν είστε κάπως πιο προχωρημένος χρήστης/γνώστης και κάποια παράγραφος σας είναι απλή ή δεν προσφέρει κάποιες παραπάνω γνώσεις σε αυτές που ήδη έχετε, μπορείτε ασφαλώς να την προσπεράσετε και να μπείτε στο "ψητό" της ιστορίας.
Μια μικρή περίληψη όσων θα δούμε μέσα στο παρόν άρθρο:
- DNS, Ευρετήριο διευθύνσεων διαδικτύου
- Διαδικασία ονοματοδοσίας
- Δυναμικός DNS για δυναμικές IP
- NoIP: Υπηρεσία δυναμικού DNS για όλους
- Γιατί το Raspberry Pi;
- Ο Dynamic Update Client της NoIP
- Μείωση του Internet Traffic
- Dynamic Updater Client script
- Μετατροπή του καινούργιου client σε υπηρεσία
- Αποτελέσματα
DNS, Ευρετήριο διευθύνσεων διαδικτύου
DNS, Domain names index of the internet
Κάθε υπολογιστής ή άλλη συσκευή που συνδέεται στο διαδίκτυο με σκοπό να ανταλλάξει πληροφορίες διαμέσου αυτού, παίρνει μια μοναδική διεύθυνση στον κόσμο. Αυτή είναι και η βασική ιδέα. Αυτή την ονομάζουμε διεύθυνση IP. Φυσικά και υπάρχουν διευθύνσεις που θα τις δούμε να υπάρχουν ταυτόχρονα σε πολλούς υπολογιστές (π.χ. μια διεύθυνση της μορφής 192.168.1.1), αλλά ας μείνουμε στη βασική ιδέα του συστήματος του διαδικτύου και ας μην επεκταθούμε σε τέτοιες λεπτομέρειες. Το router που έχουμε στο σπίτι μας ή στο γραφείο μας (ή οπουδήποτε, τέλος πάντων) είναι και αυτό μια συσκευή επικοινωνίας που συνδέεται με το διαδίκτυο. Κατά συνέπεια και αυτό έχει μια διεύθυνση IP. Γιατί γίνεται αυτό; Αυτή η διεύθυνση είναι ένα κομμάτι της "ταυτοποίησης" του αντικειμένου. Όταν γίνεται μια επικοινωνία = μεταφορά δεδομένων από ένα σύστημα σε ένα άλλο, τότε απλά οι διευθύνσεις είναι αυτές που καθορίζουν τον αποστολέα και τον παραλήπτη. Όπως ακριβώς και η μεταφορά αλληλογραφίας με το κανονικό ταχυδρομείο που χρειάζεται να φέρει ο φάκελος τις διευθύνσεις αποστολέα και παραλήπτη.
Οι διευθύνσεις που δίνονται σε όλα αυτά τα συστήματα επικοινωνίας, είτε είναι υπολογιστές είτε είναι ένας δικτυακός εκτυπωτής κ.λ.π, έχουν τη μορφή 4 αριθμών χωρισμένων από τελείες (.). Ο κάθε αριθμός μπορεί να πάρει τιμές από 0 μέχρι και 255. Υπάρχει και ένας πιο σύγχρονος τρόπος διευθυνσιοδότησης, όπου η κάθε διεύθυνση αποτελείται από 8 τετραψήφιους δεκαεξαδικούς αριθμούς χωρισμένους από άνω-κάτω τελείες (:).
Και γιατί τα αναφέρουμε όλα αυτά; Βλέπετε πουθενά λέξεις, όπως google.com, blogspot.gr ή my_really_really_fancy_site.biz; Όχι, γιατί ΔΕΝ ΥΠΑΡΧΟΥΝ. Αυτό σημαίνει πως για να μπορέσουμε να επισκεφθούμε τη μηχανή αναζήτησης της Google, το site που φιλοξενεί τα περισσότερα blogs ή μια άλλη τοποθεσία με το πιο σημαντικό υλικό που μας ενδιαφέρει (λέμε τώρα!) θα έπρεπε να θυμόμαστε ένα σωρό νούμερα που να δώσουν στον browser μας να καταλάβει ποια τοποθεσία θέλουμε να δούμε. Λίγο δύσκολο ε;... Ναι. Και για να μπορέσουμε να βρούμε τοποθεσίες στο internet θα έπρεπε να έχουμε ένα τεράστιο κατάλογο με αντιστοιχία διευθύνσεων σε ονόματα των site που αντιστοιχούν, ή ιδιότητες των site κ.λ.π. Και κάθε φορά που θα θέλαμε να επισκεφθούμε μια τοποθεσία θα έπρεπε να κατεβάζουμε τον αντίστοιχο τόμο από τη βιβλιοθήκη μας και να τον ψάχνουμε, μέχρι να βρούμε αυτή την τοποθεσία που ψάχνουμε... Ακόμα χειρότερα, κάθε φορά που ένα καινούργιο site θα δημιουργόταν στο διαδίκτυο, θα έπρεπε να ανανεώσουμε το ράφι με αυτούς τους καταλόγους!
"Χμμμ... Μα εγώ δε θυμάμαι να δίνω τέτοια νούμερα που αναφέρεις στον browser μου...". Επειδή ακριβώς το να θυμόμαστε νούμερα είναι πολύ δυσκολότερο από το να θυμόμαστε λέξεις, για να μη μπαίνουμε στον κόπο να ψάχνουμε τις διευθύνσεις που πρέπει να δώσουμε για να μπορέσουμε να προσπελάσουμε τον κάθε ιστότοπο που επιθυμούμε, έχουν δημιουργηθεί κάποιοι servers οι οποίοι κάνουν ακριβώς αυτό· αντικαθιστούν το τεράστιο ράφι της βιβλιοθήκης μας, που λέγαμε πριν... Τους δίνουμε ένα όνομα ιστότοπου και αν αυτό υπάρχει μας επιστρέφουν διεύθυνση IP στην οποία βρίσκεται ο εν λόγω ιστότοπος. Αλλά μπορούν να κάνουν και το αντίθετο, δηλαδή να μας πουν το όνομα του ιστότοπου στον οποίο οδηγεί μια διεύθυνση.
Διαδικασία ονοματοδοσίας
Naming
Και το ερώτημα είναι πώς γίνεται μια ονοματοδοσία στον τεράστιο χώρο του internet; Όταν κάποιος θελήσει να στήσει ένα server στο διαδίκτυο, τότε θα πρέπει να κατοχυρώσει ένα όνομα, το οποίο όλοι οι υπόλοιποι θα χρησιμοποιούν για να έχουν πρόσβαση σε αυτή την υπηρεσία που προσφέρει ο εν λόγω εξυπηρετητής. Αυτό το όνομα είναι μοναδικό. Αφού κατοχυρωθεί το όνομα, σε κάποιο server του διαδικτύου, μπορεί να φιλοξενηθεί η ιστοσελίδα ή η υπηρεσία που μας ενδιαφέρει. Ο server αυτός έχει κάποια συγκεκριμένη διεύθυνση IP. Μπορεί να ανήκει σε κάποια εταιρία φιλοξενίας (hosting) ιστοσελίδων, ή ακόμα και στον υπολογιστή που έχουμε στο σπίτι μας! Το τελευταίο βήμα, είναι να "παντρέψουμε" το όνομα που κατοχυρώσαμε με τη διεύθυνση στην οποία βρίσκεται η εν λόγω ιστοσελίδα/υπηρεσία· αυτή, συνήθως, τη φτιάχνουμε εμείς.
Πού βρίσκεται το πρόβλημα; Όταν το μόνο που μας ενδιαφέρει είναι να έχουμε ένα ωραιότατο server για δική μας δουλειά, θα πρέπει να κατοχυρώσουμε ένα όνομα στο διαδίκτυο (=κόστος), να πληρώσουμε σε κάποια εταιρία hosting χώρο που να τον διαμορφώσουμε κατάλληλα ώστε να φιλοξενήσει την υπηρεσία που μας ενδιαφέρει (=κόστος) και τέλος να δηλώσουμε σε κάποιο DNS ότι το όνομα που κατοχυρώσαμε "δείχνει" στον server της εταιρίας hosting (=κόστος) η οποία φιλοξενεί την υπηρεσία μας... Εναλλακτικά, μπορούμε να πληρώσουμε τον ISP μας για να μας δώσει σταθερή IP διεύθυνση, οπότε τη φιλοξενία της υπηρεσίας μας την κάνουμε στον υπολογιστή στο σπίτι μας. Όλα αυτά μας επιβαρύνουν οικονομικά.
Το ερώτημα είναι: «Αφού δεν κάνω επαγγελματική ιστοσελίδα, αλλά με ενδιαφέρει να έχω πρόσβαση στο δίκτυό μου για τη δική μου διευκόλυνση και μόνο. Δε μπορώ να το κάνω χωρίς κόστος;» Αυτό που χρειαζόμαστε είναι μια υπηρεσία που να μπορεί να μας αντιστοιχεί μια δυναμική διεύθυνση σε ένα σταθερό όνομα.
Δυναμικός DNS για δυναμικές IP
Dynamic DNS for Dynamic IP addresses
Αυτή είναι μια υπηρεσία που πλέον βρίσκεται αρκετά στο διαδίκτυο. Η εγγραφή σε μια υπηρεσία δυναμικού DNS είναι μια απλή λύση στο πρόβλημά μας. Οι περισσότερες δυναμικές DNS υπηρεσίες που προσφέρονται από κάποια sites, θέλουν να έχει κάποιος ένα κατοχυρωμένο όνομα. Άλλες πάλι, αφήνουν τον χρήστη να επιλέξει από μια σειρά ονόματα που έχει διαθέσιμα. Ένα χαρακτηριστικό παράδειγμα είναι το DynDNS.
Το DynDNS είναι ένα site που προσφέρει υπηρεσίες δυναμικού DNS. Ο χρήστης έχει τη δυνατότητα να χρησιμοποιήσει ένα όνομα της αρεσκείας του, αρκεί αυτό να ανήκει σε domains που ανήκουν στην ίδια τη DynDNS. Παράδειγμα, ένας χρήστης θα μπορούσε να χρησιμοποιεί το όνομα eliasfiles.dyndns-at-home.org γιατί ανήκει στο domain της DynDNS (dyndns-at-home.org). Το καλό που έχει το DynDNS είναι πως τα περισσότερα, πλέον, routers έχουν ενσωματωμένη την υποστήριξη για τη συγκεκριμένη υπηρεσία. Όμως, τον τελευταίο καιρό το site άρχισε να προσφέρεις τις υπηρεσίες του με πληρωμή. Πιο συγκεκριμένα, ένας καινούργιος χρήστης είναι υποχρεωμένος να δοκιμάσει την υπηρεσία DynDNSPro ελεύθερα για 14 ημέρες και αν δεν τη διακόψει νωρίτερα τότε χρεώνεται για τη χρήση της. Αυτό του δίνει το δικαίωμα να έχει την υπηρεσία για ένα χρόνο. Αν τη διακόψει πριν τη 14η ημέρα, τότε δε μπορεί να έχει καθόλου υπηρεσίες DynDNS. Για κάποιον ο οποίος θέλει να έχει πρόσβαση σε κάποια αρχεία του, κάποια βάση δεδομένων με δικές του πληροφορίες κ.λ.π. δεν είναι ότι καλύτερο να πρέπει να πληρώσει για κάτι το οποίο δεν του αποφέρει καθόλου κέρδος.
NoIP: Υπηρεσία δυναμικού DNS για όλους
NoIP: Dynamic DNS Services for all
Αρκετό ψάξιμο στο internet μου εμφάνισε σελίδες που προσέφεραν υπηρεσίες δυναμικού DNS, αλλά σε όλες έπρεπε να έχει κάποιος κατοχυρωμένο domain name. Τελικά, μια από όλες μου τράβηξε περισσότερη την προσοχή, η NoIP. Αυτή η σελίδα προσφέρει υπηρεσίες δυναμικού DNS, αλλά και ελεύθερης κατοχύρωσης ονόματος που θα ανήκει στο domain no-ip.biz (και άλλα). Κάτι παρόμοιο, δηλαδή, με το παλιό καλό DynDNS.
Τι άλλο προσφέρει η NoIP:
- Ελεύθερο (χωρίς χρέωση) Domain Name που ανήκει στο no-ip.biz (ή κάποιο άλλο από τα προκαθορισμένα του)
- Έναν client που αναλαμβάνει την ενημέρωση της λίστας του DNS με την καινούργια IP. Αυτόν τον ονομάζει DUC - Dynamic Update Client (περισσότερα ακολούθως)
- Ο DUC είναι ανοιχτού κώδικα
- Οδηγείες για τη δημιουργία δικού μας client αν δε μας αρέσει ο δικός της
Ο NoIP Client είναι ένα πρόγραμμα που τρέχει στον υπολογιστή μας και ανά τακτά διαστήματα ανιχνεύει τη διεύθυνση που έχουμε από τον ISP και αν δει ότι διαφέρει από την προηγούμενη, ενημερώνει τον DNS Server της NoIP για την καινούργια διεύθυνση. Έτσι, το όνομα το οποίο έχουμε κατοχυρώσει δείχνει στην καινούργια μας διεύθυνση. Το ίδιο ακριβώς σύστημα έχει και το DynDNS με ένα πρόγραμμα client που ονομαζόταν ddclient.
Πλεονεκτήματα της χρήσης ενός client προγράμματος είναι ότι αυτός τρέχει σε έναν υπολογιστή και ενημερώνει τη NoIP για τις αλλαγές της IP διεύθυνσης χωρίς να χρειάζεται να κάνει κάτι ο χρήστης.
Μειονεκτήματα της χρήσης ενός client είναι πως ο client λειτουργεί μόνο όση ώρα είναι σε λειτουργία ο υπολογιστής που τον τρέχει. Συνεπώς, υπάρχει η περίπτωση να πρέπει να έχουμε ένα υπολογιστή ενεργοποιημένο 24 ώρες το 24ωρο. Ο client ελέγχει για αλλαγή διεύθυνσης ανά τακτά διαστήματα. Τέτοια αλλαγή μπορεί να γίνει οποιαδήποτε ώρα και στιγμή από τον provider. Αυτό οδηγεί στο δεύτερο μειονέκτημά, πως από τον χρόνο που θα εκτελέσει ο ISP μια αλλαγή στη διεύθυνσή μας μέχρι τη στιγμή που ο client θα ελέγξει για μια τέτοια κατάσταση, ο DNS θα έχει την παλιά μας διεύθυνση και έτσι δε θα μπορούμε να έχουμε πρόσβαση στον server μας γι' αυτό το χρονικό διάστημα. Τέλος, ο client για να ανιχνεύσει την αλλαγή της διεύθυνσης, στέλνει ένα request στο server της NoIP. Αυτό σημαίνει διακίνηση δεδομένων στο διαδίκτυο, κατά συνέπεια δε μπορούμε να ρυθμίσουμε τον client να κάνει τον έλεγχο σε πολύ τακτά χρονικά διαστήματα (π.χ. 5 δευτερολέπτων). Χρήση τακτικών ελέγχων βοηθάει στο να μειώσουμε το χρόνο της ασυμφωνίας της διεύθυνσης που έχει ο DNS με την πραγματική μας, μετά από αλλαγή από τον ISP. Ταυτόχρονα, όμως, αυξάνει και τη διακίνηση πληροφορίας προς τον NoIP server. Αποτέλεσμα είναι να κινδυνεύουμε να μας κάνει τελικά ban ο server και να μη δέχεται τις πληροφορίες μας, διότι θεωρεί ότι του κάνουμε ηλεκτρονική επίθεση.
Γιατί το Raspberry Pi;
Why Raspberry Pi?
Το Raspberry Pi είναι ένας μικρός υπολογιστής μεγέθους πιστωτικής κάρτας (όχι δε μπορείτε να κάνετε πληρωμές σε καταστήματα με αυτό! :)) βασισμένος σε αρχιτεκτονική επεξεργαστή ARM. Καταναλώνει ελάχιστα και τρέχει οποιαδήποτε διανομή Linux (ή ακόμα και FreeBSD) είναι μεταγλωττισμένη γι' αυτή την αρχιτεκτονική. Είναι πολύ βολικό για τέτοιες καταστάσεις.
Επίσης, δεν υπάρχει λόγος γιατί να πρέπει να λειτουργεί 24/7 ένας υπολογιστής (εκτός και αν είναι απαραίτητο για άλλους σκοπούς). Το Raspberry Pi λόγω της μικρής του κατανάλωσης, αλλά και του μικρού του μεγέθους, μπορεί να λειτουργεί συνεχώς, χωρίς πρόβλημα, ενώ δεν πιάνει χώρο επάνω σε ένα γραφείο, όπως τα PC. Μπορεί να μπει δίπλα από το router μας (κάποια από αυτά διαθέτουν και μια USB θύρα και μπορούν να το τροφοδοτήσουν), ενώ υπάρχουν αρκετά όμορφα κουτάκια τα οποία το κάνουν να δείχνει σοβαρό σε οποιοδήποτε χώρο (μη φωνάζει η γυναίκα πως δεν ταιριάζει με τις κουρτίνες :Ρ).
Όσα περιγράφονται στο παρόν άρθρο έχουν γίνει επάνω στο λειτουργικό Raspbian που είναι βασισμένο στο Debian. Επειδή, όμως, οι διανομές Linux έχουν πάρα πολλά κοινά μεταξύ τους, δεν υπάρχει λόγος γιατί, με έστω λίγες αλλαγές, να μη μπορούν να τρέξουν και σε οποιαδήποτε άλλη διανομή για οποιαδήποτε αρχιτεκτονική.
Ο Dynamic Update Client της NoIP
NoIP Dynamic Update Client
Η NoIP δίνει έναν client για το update της διεύθυνσης IP στον DNS τους, ο οποίος είναι γραμμένος σε γλώσσα C. Για απλή χρήση είναι αυτό που χρειάζεται κάποιος. Μπορείτε να τον κατεβάσετε και να τον κάνετε compile κανονικά, όπως και κάθε άλλο πρόγραμμα. Στο Raspberry Pi γίνεται κανονικά compile, ενώ τρέχει χωρίς κανένα πρόβλημα.
Η εγκατάσταση είναι απλή. Αρχικά κατεβάζετε τον πηγαίο κώδικα, μιας και είναι opensource, από το site της NoIP. Στη συνέχεια αποσυμπιέζετε το αρχείο:
pi@raspberrypi:~ > tar -xvzf noip-duc-linux.tar.gz ... pi@raspberrypi:~ >
Αυτό θα δημιουργήσει ένα κατάλογο με όνομα noip-version. Μέσα σε αυτόν βρίσκεται όλος ο πηγαίος κώδικας, scripts για διάφορες διανομές ώστε να λειτουργέι σαν υπηρεσία κ.λ.π. Μπορείτε ελεύθερα να περιηγηθείτε μέσα στα περιεχόμενα του καταλόγου. Όταν τελικά αποφασίσετε να το εγκαταστήσετε κάνετε τα ακόλουθα: (η έκδοση που βρίσκεται ο DUC τη στιγμή που γράφεται το άρθρο είναι η 2.1.9-1)
pi@raspberrypi:~ > cd ~/noip-2.1.9-1 pi@raspberrypi:~ > make ... pi@raspberrypi:~ > sudo make install ... pi@raspberrypi:~ >
Αυτές οι εντολές θα εγκαταστήσουν το noip2 στον κατάλογο /usr/local/bin. Οι πληροφορίες που δίνονται στα διάφορα αρχεία README.FIRST είναι αρκετές για να το χρησιμοποιήσετε.
Μιας και τα μειονεκτήματα του DUC τα είδαμε πριν, ας προχωρήσουμε σε μια διαφορετική υλοποίηση από αυτή του NoIP DUC που βελτιώνει κάποια χαρακτηριστικά.
Μείωση του Internet Traffic
Internet Traffic Reduction
Η μεγαλύτερη διακίνηση πληροφορίας που γίνεται στο Internet από τον client είναι για την εύρεση της IP διεύθυνσης που μας έχει δώσει ο provider μας. Κάθε φορά που ο DUC θέλει να μάθει την IP μας, υποβάλει ένα ερώτημα στον server της NoIP. Αυτός βλέπει από ποια IP γίνεται το ερώτημα και την επιστρέφει στον DUC. Αν διαπιστωθεί αλλαγή της, τότε ο DUC στέλνει άλλο ένα request που αναφέρει πως θα πρέπει να γίνει η αλλαγή της διεύθυνσης και στον κατάλογο του DNS. (αυτή είναι η γενική ιδέα)
Επειδή η αλλαγή της διεύθυνσης συμβαίνει σε αραιά χρονικά διαστήματα (ανά μερικές ημέρες), η περισσότερη επικοινωνία που συμβαίνει (το ερώτημα της τρέχουσας IP διεύθυνσης) είναι άσκοπη. Ταυτόχρονα, σκοπός μας είναι να μπορέσουμε να υποβάλουμε ερώτημα για τη διεύθυνση IP τόσο συχνά ώστε να μην υπάρχει μεγάλο χρονικό διάστημα όπου ο DNS περιέχει παλαιά διεύθυνση (να είναι ασυγχρόνιστος).
Ο πιο απλός τρόπος είναι να μην υποβάλλεται το ερώτημα στον server της NoIP, αλλά στο router μας. Πολλά router έχουν ενεργοποιημένο telnet server, έτσι ώστε να μπορούμε να συνδεθούμε με αυτό το πρωτόκολλο και να εκτελούμε εντολές. Μια άλλη εναλλακτική υπάρχει αν το router που διαθέτουμε τρέχει ssh server, οπότε μπαίνουμε με ssh στο router και πάλι μπορούμε να εκτελέσουμε εντολές. Κάποια, βέβαια, routers δεν έχουν τίποτα από τα δυο (π.χ. κάτι oxygen)... Εκεί θα πρέπει να κάνουμε ερώτημα μέσω https (συνήθως).
Επειδή δεν είναι σταθερός ο τρόπος με τον οποίο υποβάλλουμε ερώτημα για την IP στο router, σε αυτό το άρθρο θα παρουσιαστεί ένα script που είναι για χρήση σε πρωτόκολλο telnet που είναι και το πιο συχνά εμφανιζόμενο. Συγκεκριμένα έχει δοκιμαστεί σε τρία διαφορετικά routers. Ένα ZTE (που δίνει ο ΟΤΕ), ένα TP-Link και ένα AirTies. Φυσικά, με διαφορετικό configuration. Τα δύο από τα τρία εκτελούν κανονικές εντολές Linux, ενώ το TP-Link έχει δικές του εντολές.
Αρχικά, ας δημιουργήσουμε τις συνθήκες για να κάτσουν τα script. Πρώτο βήμα είναι να φτιαχτεί ο κατάλογος που θα φιλοξενήσει τα scripts:
pi@raspberrypi:~ > sudo mkdir /usr/scripts pi@raspberrypi:~ > cd /usr/scripts pi@raspberrypi:/usr/scripts >
Ας δούμε το script που υποβάλλει ερώτημα στο router για την IP. Το script είναι γραμμένο σε python. Μιας και δεν είμαι γνώστης της python, δεν περιμένω ότι το script αυτό είναι το τελειότερο του κόσμου (λέμε τώρα!). Είναι το πρώτο script που γράφω στη συγκεκριμένη γλώσσα προγραμματισμού οπότε κάποιος έμπειρος python προγραμματιστής πολύ πιθανό να βρει κάποιες ατέλειες. Επίσης, είναι μια πρώτη έκδοση. Σκοπεύω στο μέλλον (κοντινό) να του μπαλώσω μια "τρύπα" που έχει. Αλλά για την ώρα κάνει τη δουλειά του. Μπορείτε να το κάνετε Copy/Paste στον αγαπημένο σας editor από εδώ:
#!/usr/bin/python # THE SOFTWARE IS LICENSED UNDER GPL2 GENERAL PUBLIC LICENSE v2. # IN ORDER TO OBTAIN A COPY OF THIS LICENSE PLEASE VISIT THE FOLLOWING # ADDRESS: # http://www.gnu.org/licenses/gpl-2.0.html # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #First import the necessary python libraries import sys import os import telnetlib #Setup the default values, before using the configuration file ConfFile = "/etc/GetInetIP.conf" RouterIP = "192.168.1.1" UserName = "admin" Password = "admin" NetComm = "ifconfig ppp0" AddrPrefix = "inet " ExpTime = 0.0001 #If the user has entered a CLI argument, use it as the configuration file to be used InArgs = len(sys.argv) if InArgs > 2: sys.stderr.write("ERROR: Only one input argument expexted!\n") sys.stderr.write("Usage: GetInetIP [configuration_filename]\n"); exit(1) elif InArgs == 2: ConfFile = sys.argv[1] #Need to check if the user specified filename exists. If not => Error if not(os.path.exists(ConfFile)): sys.stderr.write("ERROR: Configuration file" + ConfFile + "not found\n") exit(2) #Check for the existance of the configuration file #Time to import the configuration file's values. If the user specified a configuration file # then it really exists. But if not, we must try to load the configuration file from # the default path. If this file doesnot exist, then we ave to use our hardcoded values #NOTE:The execfile command should be changed for security reasons... A more secure approach # is to parse the configuration file, and not execute it... (no time to fixit right now) if os.path.exists(ConfFile): execfile(ConfFile) #Connect to the specified router using telnet protocol Conn = "" try: Conn = telnetlib.Telnet(RouterIP) except: sys.stderr.write("ERROR: Cannot communicate with" + RouterIP +"\n") exit(3) #Send the login credentials RespStr = "First" FailCount = 3 while (RespStr != "") and (FailCount > 0): RespStr = Conn.read_until("\n",ExpTime) LoginFlag = RespStr.find("login:") if LoginFlag >= 0: Conn.write(UserName + "\n") RespStr = Conn.read_until("\n",ExpTime) PassFlag = RespStr.find("Password:") if PassFlag >= 0: Conn.write(Password + "\n") RespStr = Conn.read_until("\n",ExpTime) FailCount -= 1 if FailCount == 0: sys.stderr.write("ERROR: Credentials failed 3 times\n") exit(4) #Now the telnet awaits commands! Issue the command that announces the IP needed Conn.write(NetComm +"\n") RespStr = Conn.read_until("\n",ExpTime) InetFlag = False while (RespStr != "") and not(InetFlag): InetStrt = RespStr.find(AddrPrefix) if InetStrt >= 0: InetFlag = True InetStrt += len(AddrPrefix) InetEnd = RespStr.find(" ",InetStrt) else: RespStr = Conn.read_until("\n",ExpTime) Conn.write("exit\n") DummyStr = Conn.read_all() Conn.close() if not(InetFlag): sys.stderr.write("ERROR: IP Address not found\n") exit(5) OutStr = RespStr[InetStrt:InetEnd] print(OutStr.strip()) exit(0)
Αυτό το script το σώζουμε με το όνομα GetInetIP.py μέσα στον κατάλογο που φτιάξαμε. Για να λειτουργήσει χρειάζεται και ένα configuration αρχείο μέσα στον κατάλογο /etc. Προσοχή χρειάζεται στο ότι στο configuration αρχείο βρίσκονται στοιχεία που δεν πρέπει να φτάσουν στα μάτια τρίτων! Κοινώς, το password του router... Το αρχείο θα πρέπει να ονομάζεται GetInetIP.conf:
Configuration για router TP-Link
Configuration for TP-Link router
RouterIP = "192.168.1.1" Password = "MyCrazyPassword" NetComm = "show wan status" AddrPrefix = "Ip = " ExpTime = 0.07
Ας δούμε λίγο τα περιεχόμενα του configuration αρχείου:
- RouterIP: Δηλώνει τη διεύθυνση IP που έχει το router μας
- UserName: Το όνομα χρήστη για το router. Αν δεν δηλώνεται εδώ χρησιμοποιείται η default τιμή που είναι το admin. Κάποια από τα routers δε ζητάνε το username. Σε αυτή την περίπτωση το script το καταλαβαίνει και δεν το στέλνει.
- Password: Το password που χρειαζόμαστε για να κάνουμε login στο router
- NetComm: Η εντολή που εκτελούμε, όταν κάνουμε telnet στο router που μας δείχνει τη διεύθυνση IP που έχει στο WAN. Για το TP-Link είναι show wan status, ενώ για ένα router που τρέχει busybox ή γενικά εκτελεί εντολές Linux είναι κάτι του στυλ ifconfig ppp0
- AddrPrefix: Όποια και αν είναι η εντολή που μας δίνει την επιθυμητή πληροφορία, δεν δίνει απλά ένα ξερό νούμερο, αλλά ολόκληρο κείμενο. Εδώ περιγράφεται τι περιμένει το script για να βρει ακριβώς πριν τη διεύθυνση IP που μας ενδιαφέρει. Αμέσως μετά από αυτό το κείμενο αναφέρεται η πληροφορία μας.
- ExpTime: Είναι ο χρόνος μέσα στον οποίο αν το router δεν στείλει κάποιο χαρακτήρα, θεωρείται πώς δεν έχει κάτι άλλο να στείλει. Προσοχή, σε άλλα router είναι μικρότερος και σε άλλα μεγαλύτερος. Γενικά, ο μεγάλος χρόνος κάνει για όλα τα router, αλλά καθυστερεί την εξέλιξη του script.
Αν μπούμε χειροκίνητα σε ένα TP-Link router και δώσουμε την εντολή show wan status τότε θα δούμε τα ακόλουθα:
eliaschr@orion:~> telnet router Trying 192.168.1.1... Connected to router. Escape character is '^]'. Password: ********************* Copyright (c) 2001 - 2011 TP-LINK TECHNOLOGIES CO., LTD. TP-LINK> show wan status PVC-0 Status = Up PVC-1 Status = Up PVC-2 Status = Up PVC-3 Status = Up PVC-4 Status = Up Ip = 94.71.120.43 PVC-5 Status = Up PVC-6 Status = Up PVC-7 Status = Down TP-LINK>
Βλέπουμε ότι ακριβώς πριν την IP που μας ενδιαφέρει βρίσκεται το Ip = . Αυτό είναι που βάζουμε στο AddrPrefix.
Στην περίπτωση του ZTE και του AirTies ένα configuration αρχείο σαν αυτό που ακολουθεί κάνει τη δουλειά:
Configuration για router ZTE - AirTies - Linux style commands
Configuration for ZTE - AirTies routers - Linux style commands
RouterIP = "192.168.1.1" Password = "MyCrazyPassword" NetComm = "ifconfig ppp0" AddrPrefix = "inet addr:" ExpTime = 0.05
Φυσικά, όποιο και να είναι το configuration που θα επιλέξετε, το μόνο σίγουρο είναι πως δεν πρέπει να το διαβάζει κανένας, παρά μόνο ο root!:
pi@raspberrypi:/usr/scripts > chown root:root /etc/GetInetIP.conf pi@raspberrypi:/usr/scripts > chmod 0600 /etc/GetInetIP.conf pi@raspberrypi:/usr/scripts >
Dynamic Updater Client script
Ωραία! Αφού φτιάξαμε το script που μας διαβάζει την IP από το router μας, μένει να φτιάξουμε και το script που θα κάνει τη δουλειά του NoIP DUC. Δημιουργήστε το αρχείο IP-Updater.sh στον κατάλογο /usr/scripts που φτιάξαμε πριν. Το αρχείο αυτό θα πρέπει να το κάνετε Copy/Paste από εδώ:
#!/bin/bash # THE SOFTWARE IS LICENSED UNDER GPL2 GENERAL PUBLIC LICENSE v2. # IN ORDER TO OBTAIN A COPY OF THIS LICENSE PLEASE VISIT THE FOLLOWING # ADDRESS: # http://www.gnu.org/licenses/gpl-2.0.html # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # The script uses a configuration file that is called /etc/IP-Updater.conf # Valid lines in the script are: # NoIPUser=the_username_of_NoIP_login_account # NoIPPass=the_password_of_the_account # NoIPHost=the_host_to_which_the_IP_is_going_to_be_updated # FindIPComm=/path/to/the_command_line_to_be_executed_to_get_the_IP # FailIPDelay=Delay in seconds before retrying to find out the new # IP address, after a failure # RepeatDelay=Delay in seconds before trying to refresh the IP after a # successful update # FailDelay=Delay in seconds if there is a fatal error in NoIP. (Only if PreventExit=true) # WGetDelay=Delay in seconds if there is a failure in wget to NoIP # WGetLongFailDelay=Delay in seconds after Maximum number of failures reached # WGetMaxFails=Maximum wget sequensial failures that make the daemon exit (unless # PreventExit=true # MaxFailIPCnt=Maximum number of retries to fetch IP address from router # while it seems to have connection to the internet # PreventExit={true/false} If true, then upon fatal error the script never ends # SuccessCmd=Command to execute after a successful update to No-IP # FailCmd=Command to execute after a failed NoIP update # ExitCmd=Command to execute if the program must exit because of Fatal Error # In ExitCmd, FailCmd and SuccessCmd you can use the following values as input: # $NoIPResp: if you want to include the responce from NoIP after the # request of IP Address updating # $NewIP: the new IP that found and sent to NoIP # $CurrIP: the old IP that the system had before the update # $CurDate: the time that the execution of the IP update happened PIDFILE=/var/run/IP-Updater.pid PID=$BASHPID LOGME="logger -ist 'NoIP-Updater'" TEMPFILE="/tmp/CurrentIP" TEMPWGET="/tmp/CurrentIP.wget" CONFFILE="/etc/IP-Updater.conf" USERAGENT="WGet from Raspberry Pi Linux/1.0 eliaschr@gmail.com" NoIPUser="" NoIPPass="" NoIPHost="" FindIPComm="echo 'ERROR: No router IP fetch program specified' >&2; exit 4;" PreventExit=true FailDelay=1800 MaxFailIPCnt=3 FailIPDelay=15 RepeatDelay=60 WGetDelay=5 WGetLongFailDelay=60 WGetMaxFails=10 SuccessCmd="" FailCmd="" ExitCmd="" WGetErrMess[1]="Generic error code" WGetErrMess[2]="Parse error" WGetErrMess[3]="File I/O error" WGetErrMess[4]="Network failure" WGetErrMess[5]="SSL verification failure" WGetErrMess[6]="Username/password authentication failure" WGetErrMess[7]="Protocol errors" WGetErrMess[8]="Server issued an error response" CurrIP="x" test -r $CONFFILE || ( echo "Failed to load the configuration file $CONFFILE..." >&2; exit 1 ) source $CONFFILE touch $TEMPFILE echo "" > $TEMPFILE touch $PIDFILE echo $PID > $PIDFILE while true do source $TEMPFILE FailCounter=$MaxFailIPCnt NextDelay=$RepeatDelay NewIP=`$FindIPComm` Status=$? while (( $Status != 0 )) do CurDate=`date +%T` $LOGME "$CurDate WARNING: Could not read the IP address" if (( $FailCounter == 0 )) then $LOGME "Too many failures... Abording" rm $PIDFILE exit 2 fi sleep $FailIPDelay PingStat=`ping -c1 www.no-ip.com > /dev/null; echo $?` if [[ "$PingStat" == "0" ]] then NewIP=`$FindIPComm` Status=$? FailCounter=$((FailCounter -1)) fi done if [[ "$CurrIP" != "$NewIP" ]] then CurDate=`date +%T` WGetStatus=`wget -qO $TEMPWGET -U "$USERAGENT" --user=$NoIPUser --password=$NoIPPass "http://dynupdate.no-ip.com/nic/update?hostname=${NoIPHost}&myip=$NewIP"; echo $?` NoIPResp=`cat $TEMPWGET` if (( $WGetStatus == 0 )) then WGetFails=$WGetMaxFails case ${NoIPResp/ */} in "good") FailIPResult=0 echo "CurrIP=$NewIP" > $TEMPFILE $LOGME "IP Address successfuly updated" ;; "nochg") FailIPResult=1 HostIPResp=`host $NoIPHost` if (( $? == 0 )) then HostIP=${HostIPResp/* /} if [[ "$HostIP" == "$NewIP" ]] then FailIPResult=0 Comment="successfuly" echo "CurrIP=$HostIP" > $TEMPFILE else Comment="but it seems different than expected..." NextDelay=$FailIPDelay fi else Comment="but could not resolve $NoIPHost" NextDelay=$FailIPDelay fi $LOGME "$CurDate WARNING: The IP in NoIP was updated by another process $Comment" ;; "nohost") FailIPResult=1 $LOGME "$CurDate ERROR: Hostname supplied does not exist under specified account" ;; "badauth") FailIPResult=1 $LOGME "$CurDate ERROR: Invalid username password combination" ;; "badagent") FailIPResult=2 $LOGME "$CurDate ERROR: Client disabled. Client should exit and not perform any more updates without user intervention" ;; "\!donator") $LOGME "$CurDate ERROR: An update request was sent including a feature that is not available to that particular user" FailIPResult=1 ;; "abuse") $LOGME "$CurDate ERROR: Username is blocked due to abuse. Either for not following our update specifications or disabled due to violation of the No-IP terms of service" FailIPResult=1 ;; "911") $LOGME "$CurDate FATAL ERROR: NoIP.com error such as a database outage. Retry the update no sooner 30 minutes" FailIPResult=1 NextDelay=1800 ;; *) $LOGME "$CurDate ERROR: Unknown responce from NoIP.com - $NoIPResp" FailIPResult=1 ;; esac case $FailIPResult in 0) eval $SuccessCmd ;; 1) eval $FailCmd ;; *) if $PreventExit then NextDelay=$FailDelay eval $FailCmd else eval $ExitCmd rm $PIDFILE exit 3 fi ;; esac else $LOGME "$CurDate ERROR: Wget exited with error status $WGetStatus: ${WGetErrMess[$WGetStatus]}" WGetFails=$((WGetFails -1)) if (( $WGetFails == 0 )) then WGetFails=1 NextDelay=$WGetLongFailDelay else NextDelay=$WGetDelay fi fi fi sleep $NextDelay done
Αυτό το script είναι γραμμένο σε bash shell. Και αυτό για να λειτουργήσει χρειάζεται το configuration αρχείο του, /etc/IP-Updater.conf. Ένα δείγμα του εν λόγω αρχείου φαίνεται ακολούθως:
NoIPUser=noipusermail@myisp.gr NoIPPass="MyUltraFancyPassword!" NoIPHost=myhostname.no-ip.biz FindIPComm="/usr/scripts/GetInetIP.py" FailIPDelay=15 RepeatDelay=60 FailDelay=1800 MaxFailIPCnt=3 PreventExit=true SuccessCmd="/usr/bin/ttytter -keyf=/etc/ttytter/Servertty -silent -status=\"\`date\` IP Updated successfully. NoIP Response is: \$NoIPResp\"" FailCmd="/usr/bin/ttytter -keyf=/etc/ttytter/Servertty -silent -status=\"\`date\` IP Update failed. NoIP Response is: \$NoIPResp\"" ExitCmd="/usr/bin/ttytter -keyf=/etc/ttytter/Servertty -silent -status=\"\`date\` Fatal Error during IP Update. NoIP response is: \$NoIPResp. Exiting...\""
Ας δουμε μια λίστα με πιθανές παραμέτρους του αρχείου:
- NoIPUser: Δηλώνει τη διεύθυνση mail του λογαριασμού του χρήστη που έχουμε φτιάξει στο NoIP
- NoIPPass: Το password που έχουμε στο NoIP λογαριασμό
- NoIPHost: Το όνομα του domain που έχουμε καταχωρήσει
- FindIPComm: Δηλώνεται το script που όταν τρέξει μας δίνει τη διεύθυνση IP του router. Κοινώς, το script που φτιάξαμε πριν σε python
- FailIPDelay: Όταν δεν καταφέρει να διαβάσει την IP από το router, όπως σε περίπτωση που το router δεν έχει πάρει ακόμα IP μετά από διακοπή ρεύματος, ή το router είναι σβηστό, το script περιμένει όσα δευτερόλεπτα ορίζονται σε αυτή την παράμετρο, πριν ξαναπροσπαθήσει
- RepeatDelay: Όταν διαβάσει σωστά μια IP διεύθυνση, δηλώνει σε πόσο χρόνο θα ξαναπροσπαθήσει να δει αν υπάρχει αλλαγή της
- FailDelay: Όταν υπάρξει πρόβλημα στην ενημέρωση της καινούργιας IP, όπως σε περίπτωση που το NoIP επιστρέψει "911", το script περιμένει όσο χρόνο δηλώνεται εδώ σε δευτερόλεπτα πριν ξαναπροσπαθήσει την ενημέρωση του NoIP DNS
- MaxFailIPCnt: Ο μέγιστος αριθμός προσπαθειών στις οποίες δε θα μπορέσει να διαβάσει την IP διεύθυνση από το router, ενώ πιστοποιείται πως υπάρχει σύνδεση με το internet. Αυτή η κατάσταση κανονικά δεν πρέπει να υπάρξει ποτέ.
- PreventExit:true ή false. Αν είναι true τότε το script δε σταματάει ποτέ, ούτε σε περίπτωση λάθους όπως π.χ. username/password στο NoIP
- SuccessCmd: Εντολή που θα εκτελείται όταν έχουμε μια σωστή απάντηση από το NoIP όσον αφορά την ανανέωση της IP μας. Στο παράδειγμα φαίνεται να τρέχει το ttytter με τέτοιο τρόπο ώστε να μπορεί να στέλνει tweet την κατάστασή του. Δώστε βάση στον τρόπο με τον οποίο δηλώνονται μεταβλητές και εσωτερικές εκτελέσεις εντολών, όπως η date
- FailCmd: Το ίδιο, αλλά για την περίπτωση όπου υπάρξει απάντηση σφάλματος από το NoIP
- ExitCmd: Ομοίως για την περίπτωση όπου υπάρξει τέτοιο σφάλμα όπου θα πρέπει το script να τερματιστεί
- WGetDelay: Το script χρησιμοποιεί τη wget για να στείλει το request στο NoIP server. Σε περίπτωση που υπάρξει σφάλμα περιμένει όσο χρόνο του ορίζει αυτή η μεταβλητή
- WGetMaxFails: Όταν υπάρξουν τόσα σφάλματα στη σειρά από τη wget όσα ορίζονται σε αυτή την παράμετρο, τότε ο χρόνος αναμονής μέχρι το επόμενο wget που θα επιχειρηθεί ορίζεται από την WGetLongFailDelay
- WGetLongFailDelay: Μετά από WGetMaxFails αποτυχίες της wget ο χρόνος που περιμένει πριν ξαναδοκιμάσει να στείλει request ορίζεται από αυτή την παράμετρο
Το script μας δίνει τη δυνατότητα να εκτελέσουμε κάποιες εντολές, ανάλογα με το αν ήταν επιτυχής η αλλαγή της IP, αν υπήρξε κάποιο σφάλμα, ή αν υπήρξε μια ανεπίτρεπτη κατάσταση που οδηγεί το script σε τερματισμό. Οι εντολές αυτές ορίζονται στις παραμέτρους SuccessCmd, FailCmd και ExitCmd. Σε αυτές τις εντολές, μπορεί να θέλουμε να χρησιμοποιήσουμε κάποιες πληροφορίες από το script. Οι δυνατές περιπτώσεις είναι οι:
- $NoIPResp: Περιέχει την απάντηση από τον server της NoIP
- $NewIP: Περιέχει την καινούργια IP διεύθυνση
- $CurrIP: Περιέχει την παλιά IP διεύθυνση
- $CurDate: Περιέχει την ώρα που ανιχνεύτηκε η αλλαγή της IP
Και φυσικά, μιας και σε αυτό το αρχείο περιέχονται ευαίσθητες πληροφορίες, θα πρέπει να μπορεί να το διαβάσει μόνο ο root:
pi@raspberrypi:/usr/scripts > chown root:root /etc/IP-Updater.conf pi@raspberrypi:/usr/scripts > chmod 0600 /etc/IP-Updater.conf pi@raspberrypi:/usr/scripts >
Τέλος, μιας και αυτά τα δύο scripts είναι βασικά για τη λειτουργία του συστήματός μας, καλό είναι και αυτά να τα κάνουμε μη προσβάσιμα από τους χρήστες (εκτός του root):
pi@raspberrypi:/usr/scripts > sudo chown -R root:root /usr/scripts pi@raspberrypi:/usr/scripts > sudo chmod -R 0700 /usr/scripts pi@raspberrypi:/usr/scripts >
Μετατροπή του καινούργιου client σε υπηρεσία
Conversion of the new client to a system service
Το τελικό στάδιο είναι να εκτελείται το script που φτιάξαμε ως υπηρεσία του συστήματος. Κάποιος θα μπορούσε πολύ απλά να το τρέξει μέσα από τον cron. Αυτό όμως δεν θεωρείται και τόσο καλή ιδέα. Είναι σαφώς καλύτερα να λειτουργεί όπως όλες οι υπηρεσίες του συστήματος, να ενεργοποιείται και να απενεργοποιείται ανάλογα με το runlevel κ.λ.π. Για να το πετύχουμε αυτό θα πρέπει να φτιάξουμε ένα ακόμα script. Μια παρόμοια εργασία κάναμε και σε προηγούμενο άρθρο. Θα ακολουθήσουμε, λοιπόν, την ίδια τακτική.
Κάντε Copy/Paste τον ακόλουθο κώδικα στον αγαπημένο σας editor:
#! /bin/sh ### BEGIN INIT INFO # Provides: IP-Updater # Required-Start: $local_fs $syslog $network # Required-Stop: $local_fs $syslog $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: IP Updater for NoIP Dynamic DNS service # Description: Start IP Updater to check the IP that the ISP has given to router. # When the IP is changed, the updater informs NoIP and updates the chosen domain name # to point to the new IP. ### END INIT INFO # Author: Elias Chrysocheris# # Do NOT "set -e" # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="IP Updating client service for NoIP (http://www.noip.com/)" NAME=IP-Updater DAEMON=/usr/scripts/IP-Updater.sh SCRIPTNAME=/etc/init.d/$NAME PIDFILE=/var/run/IP-Updater.pid # Exit if the package is not installed [ -e "$DAEMON" ] || { echo "Cannot find IP-Updater executable in $DAEMON. Perhaps it is not installed..."; if [ "$1" = "stop" ]; then exit 0; else exit 5; fi; } # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.2-14) to ensure that this file is present # and status_of_proc is working. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { # Return # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started start-stop-daemon --test --start --quiet --pidfile $PIDFILE --exec $DAEMON > /dev/null \ || return 1 start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON & RETVAL="$?" [ "$RETVAL" > 2 ] && return 2 # Add code here, if necessary, that waits for the process to be ready # to handle requests from services started subsequently which depend # on this one. As a last resort, sleep for some time. } # # Function that stops the daemon/service # do_stop() { # Return # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # other if a failure occurred start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE RETVAL="$?" [ "$RETVAL" = 2 ] && return 2 # Wait for children to finish too if this is a daemon that forks # and if the daemon is only ever run from this initscript. # If the above conditions are not satisfied then add some other code # that waits for the process to drop all resources that could be # needed by services started subsequently. A last resort is to # sleep for some time. start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --pidfile $PIDFILE [ "$?" = 2 ] && return 2 rm $PIDFILE return "$RETVAL" } # # Function that sends a SIGHUP to the daemon/service # do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; #reload|force-reload) # # If do_reload() is not implemented then leave this commented out # and leave 'force-reload' as an alias for 'restart'. # #log_daemon_msg "Reloading $DESC" "$NAME" #do_reload #log_end_msg $? #;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 exit 3 ;; esac
Το script βασίστηκε για άλλη μια φορά στο κλασικό skeleton που υπάρχει στον κατάλογο /etc/init.d/. Εκεί θα το σώσουμε σαν root με το όνομα IP-Updater. Αν δεν έχουμε τη δυνατότητα να το κάνουμε απ' ευθείας, μπορούμε σαν χρήστες να το σώσουμε στον κατάλογό μας και στη συνέχεια να το αντιγράψουμε ως root στην κατάλληλη θέση. Στη συνέχεια αλλάζουμε τα δικαιώματα και το κάνουμε εκτελέσιμο και ενημερώνουμε το σύστημα για την καινούργια υπηρεσία:
pi@raspberrypi:/usr/scripts > cd /etc/init.d/ pi@raspberrypi:/etc/init.d > sudo cp /home/pi/IP-Updater . pi@raspberrypi:/etc/init.d > sudo chmod 0755 IP-Updater pi@raspberrypi:/etc/init.d > sudo update-rc.d IP-Updater defaults pi@raspberrypi:/etc/init.d >
Μετά από αυτό μπορούμε να εκκινήσουμε την υπηρεσία μας. Όμως, για να δοκιμάσουμε ότι όλα βαίνουν καλώς, καλό είναι να δούμε αν ενεργοποιείται αυτόματα με μια επανεκκίνηση:
pi@raspberrypi:/usr/scripts > sudo shutdown -r now pi@raspberrypi:/etc/init.d >
Αποτελέσματα
Conclusions
Εαν όλα έχουν πάει καλά, τότε μπορείτε να παρακολουθήσετε τα μηνύματα στο /var/log/messages και να δείτε τη λειτουργία του script. Αν προσέξατε, στο configuration αρχείο του IP-Updater, χρησιμοποιείται το ttytter που είναι ένας client για το twitter. Μέσω αυτού βλέπουμε on-line τη λειτουργία της αλλαγής διεύθυνσης
Αυτό φυσικά προϋποθέτει να έχετε στήσει το ttytter και να το έχετε ρυθμίσει.
Έπειτα από αρκετό καιρό που λειτουργεί η υπηρεσία του NoIP στο Raspberry Pi δεν έχω πετύχει κατάσταση που να μη μπορώ να έχω πρόσβαση μέσω του δικτύου, απομακρυσμένα. Η απόκριση του συστήματος είναι παραπάνω από ικανοποιητική.