‏הצגת רשומות עם תוויות linux. הצג את כל הרשומות
‏הצגת רשומות עם תוויות linux. הצג את כל הרשומות

יום שבת, 1 בנובמבר 2014

Deep Packet Inspection With C



מערכות FireWall בדר"כ בוחנות את החבילות שעוברות דרכן רק על ידי אימות של כתובות ופורטים מול רשימת חוקים, אבל עם הזמן הבינו שזה לא מספיק טוב ויש לנתח את המידע עצמו שעובר בחבילה על מנת לזהות התקפות שמשפיעות על השירות עצמו, נדרשה חשיבה חדשה ועם הזמן צצו מערכות IDS \ IPS שבעזרתם ניתן לבצע בדיקות שמבוססות על תבניות ולחפש קוד זדוני בתוך החבילה, פרוייקט מאוד מפורסם ונמצא בשימוש רחב במערכות כאלו נקרא Snort שמכיל המון תבניות להתקפות מוכרות.

שימו לב!
  • המאמר נכתב על Fedora 13.
  • הקוד נערך ב Eclipse.
כל חבילה שמגיעה למכונה עוברת בדיקה מול החוקים שהוגדרו ב iptables שעובד מול ה NetFilter שיושב ב Kernel, אם החוקים מאפשרים להעביר את החבילה ליעד ה Netfilter יאשר את החבילה (NF_ACCEPT) אבל במקרה שהחוקים חוסמים אותה הוא יפיל את החבילה (NF_DROP), אלה 2 הפעולות הבסיסיות שנמצאות בכל Firewall אבל זה לא מספיק וצריך לחפור יותר פנימה, ניתן לרשום חוק שמפנה את המידע (NF_QUEUE) למחסנית שמאפשרת גישה לתוכניות מעולם ה UserSpace לנתח את החבילות ולחרוץ את גורלם.

התוכנית קובעת את גורל החבילה

לצורך הדוגמה החוק הבא מכניס את כל המידע שמגיע למחסנית במיקום 0 אבל במקרה של עבודה אינטנסיבית המחסנית תתמלא עוד לפני שנספיק לנקות אותה ולכן החוק הזה לא כלכך יעיל:

#: iptables -A INPUT  -j NFQUEUE --queue-num 0 --queue-balance

עבור ניתוח יעיל יותר צריכים לעבוד במקביל, ניתן לפצל חוק בין מספר מחסניות שונות כפי שניתן לראות בדוגמה:

#: iptables -A INPUT  -j NFQUEUE --queue-balance 0:3
#: iptables -A OUTPUT  -j NFQUEUE --queue-balance 4:8


Libnetfilter_queue Library

ספריה רשמית שמאפשרת לנהל את החבילות שנמצאות במחסניות בעזרת עבודה מול Netlink דרך Unix Domain Socket, היא מחליפה ספרייה ישנה יותר שנקראת ip_queue והיא נמצאת כמעט בכל Distribution של Linux, אבל בשביל לעבוד איתה יש להוריד את ה Headers ולהכין את ה Eclipse:

#: yum install libnetfilter_queue
#: yum install libnetfilter_queue-devel


נשלב את הספריות המתאימות ב Linker:





קוד

פונקצית Main מייצרת מערך של Threads שכל אחד מהם מתחבר ל Queue אחר, כאשר תגיע חבילה לאחת מהמחסניות, פונקציית packetHandler תרוץ ודרכה ננתח את המידע, לצורך הדוגמה כאשר נזהה את המילה facebook בתוכן החבילה נפיל אותה ונחסום את הגישה.

filterQueue.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <string.h>

/* for ethernet header */
#include<net/ethernet.h>

/* for UDP header */
#include<linux/udp.h>

/* for TCP header */
#include<linux/tcp.h>

/* for IP header */
#include<linux/ip.h>

/*  -20 (maximum priority) */
#include <sys/time.h>
#include <sys/resource.h>

/* for NF_ACCEPT */
#include <linux/netfilter.h>

/* for Threads */
#include <pthread.h>

/* for Queue */
#include <libnetfilter_queue/libnetfilter_queue.h>

#define NUM_THREADS     15

pthread_t threads[NUM_THREADS];

void printTCP(unsigned char *buffer) {

unsigned short iphdrlen;

struct iphdr *iph = (struct iphdr *) (buffer + sizeof(struct ethhdr));
iphdrlen = iph->ihl * 4;

struct tcphdr *tcph = (struct tcphdr *) (buffer + iphdrlen
+ sizeof(struct ethhdr));

int header_size = sizeof(struct ethhdr) + iphdrlen + tcph->doff * 4;

printf("| Packet Type: TCP \n");
printf("|-Source Port      : %u\n", ntohs(tcph->source));
printf("|-Destination Port : %u\n", ntohs(tcph->dest));
printf("|-Sequence Number    : %u\n", ntohl(tcph->seq));
printf("|-Acknowledge Number : %u\n", ntohl(tcph->ack_seq));
printf("|-Header Length      : %d DWORDS or %d BYTES\n",
(unsigned int) tcph->doff, (unsigned int) tcph->doff * 4);
printf("|-CWR Flag : %d\n", (unsigned int) tcph->cwr);
printf("|-ECN Flag : %d\n", (unsigned int) tcph->ece);
printf("|-Urgent Flag          : %d\n", (unsigned int) tcph->urg);
printf("|-Acknowledgement Flag : %d\n", (unsigned int) tcph->ack);
printf("|-Push Flag            : %d\n", (unsigned int) tcph->psh);
printf("|-Reset Flag           : %d\n", (unsigned int) tcph->rst);
printf("|-Synchronise Flag     : %d\n", (unsigned int) tcph->syn);
printf("|-Finish Flag          : %d\n", (unsigned int) tcph->fin);
printf("|-Window         : %d\n", ntohs(tcph->window));
printf("|-Checksum       : %d\n", ntohs(tcph->check));
printf("|-Urgent Pointer : %d\n", tcph->urg_ptr);
}

void printUDP(unsigned char *buffer) {
unsigned short iphdrlen;

struct iphdr *iph = (struct iphdr *) (buffer + sizeof(struct ethhdr));
iphdrlen = iph->ihl * 4;

struct udphdr *udph = (struct udphdr*) (buffer + iphdrlen
+ sizeof(struct ethhdr));

int header_size = sizeof(struct ethhdr) + iphdrlen + sizeof udph;

printf("| Packet Type: UDP \n");
printf("|-Source Port      : %u\n", ntohs(udph->source));
printf("|-Destination Port : %u\n", ntohs(udph->dest));
printf("|-UDP Length : %u\n", ntohs(udph->len));
printf("|-UDP Checksum : %u\n", ntohs(udph->check));

}

char * getText(unsigned char * data, char Size) {

char * text = malloc(Size);
int i = 0;

for (i = 0; i < Size; i++) {
if (data[i] >= 32 && data[i] <= 128)
text[i] = (unsigned char) data[i];
else
text[i] = '.';
}
return text;

}

u_int32_t analyzePacket(struct nfq_data *tb, int *blockFlag) {

//packet id in the queue
int id = 0;

//the queue header
struct nfqnl_msg_packet_hdr *ph;

//the packet
char *data;

//packet size
int ret;

//extracting the queue header
ph = nfq_get_msg_packet_hdr(tb);

//getting the id of the packet in the queue
if (ph)
id = ntohl(ph->packet_id);

//getting the length and the payload of the packet
ret = nfq_get_payload(tb, &data);
if (ret >= 0) {

printf("Packet Received: %d \n", ret);

/* extracting the ipheader from packet */
struct sockaddr_in source, dest;
unsigned short iphdrlen;

struct iphdr *iph = ((struct iphdr *) data);
iphdrlen = iph->ihl * 4;

memset(&source, 0, sizeof(source));
source.sin_addr.s_addr = iph->saddr;

memset(&dest, 0, sizeof(dest));
dest.sin_addr.s_addr = iph->daddr;

printf("|-Source IP: %s\n", inet_ntoa(source.sin_addr));
printf("|-Destination IP: %s\n", inet_ntoa(dest.sin_addr));
printf("|-Checking for Protocol: \n");

if (iph->protocol == 6) {
printTCP(data);
} else if (iph->protocol == 17) {
printUDP(data);
}

printf("|-Extracting Payload: \n");

char * text = getText(data, ret);

//filtering requests for facebook
if (text && text[0] != '\0') {
printf("\n %s \n", text);
ret = strstr(text, "facebook");
if (ret == 0)
//not found in string
*blockFlag = 0;
else
//found in string
*blockFlag = 1;
}

//release the packet
free(text);


}
//return the queue id
return id;

}

int packetHandler(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa,
void *data) {

printf("entering callback \n");

//when to drop
int blockFlag = 0;

//analyze the packet and return the packet id in the queue
u_int32_t id = analyzePacket(nfa, &blockFlag);

//this is the point where we decide the destiny of the packet
if (blockFlag == 0)
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
else
return nfq_set_verdict(qh, id, NF_DROP, 0, NULL);



}

void *QueueThread(void *threadid) {

//thread id
long tid;
tid = (long) threadid;


struct nfq_handle *h;
struct nfq_q_handle *qh;
char buf[128000] __attribute__ ((aligned));

//pointers and descriptors
int fd;
int rv;
int ql;


printf("open handle to the netfilter_queue - > Thread: %d \n", tid);
h = nfq_open();
if (!h) {
fprintf(stderr, "cannot open nfq_open()\n");
return NULL;
}

//unbinding previous procfs
if (nfq_unbind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_unbind_pf()\n");
return NULL;
}

//binding the netlink procfs
if (nfq_bind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_bind_pf()\n");
return NULL;
}

//connet the thread for specific socket
printf("binding this socket to queue '%d'\n", tid);
qh = nfq_create_queue(h, tid, &packetHandler, NULL);
if (!qh) {
fprintf(stderr, "error during nfq_create_queue()\n");
return NULL;
}

//set queue length before start dropping packages
ql = nfq_set_queue_maxlen(qh, 100000);

//set the queue for copy mode
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
fprintf(stderr, "can't set packet_copy mode\n");
return NULL;
}

//getting the file descriptor
fd = nfq_fd(h);

while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
printf("pkt received in Thread: %d \n", tid);
nfq_handle_packet(h, buf, rv);
}

printf("unbinding from queue Thread: %d  \n", tid);
nfq_destroy_queue(qh);

printf("closing library handle\n");
nfq_close(h);

return NULL;

}

int main(int argc, char *argv[]) {

//set process priority
setpriority(PRIO_PROCESS, 0, -20);

int rc;
long balancerSocket;
for (balancerSocket = 0; balancerSocket < NUM_THREADS; balancerSocket++) {
printf("In main: creating thread %ld\n", balancerSocket);

//send the balancer socket for the queue
rc = pthread_create(&threads[balancerSocket], NULL, QueueThread,
(void *) balancerSocket);

if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}

while (1) {
sleep(10);
}

//destroy all threads
pthread_exit(NULL);
}

טיפ קטן

אם רוצים לעקוב אחרי העומסים במחסניות ניתן לקרוא את הקובץ nfnetlink_queue:

/proc/net/netfilter


סיכום

ניתוח המידע הוא הכרחי מאוד לאור כל הסיכונים שקיימים היום, היכולת לחרוץ את גורל החבילה מחוץ ל Kernel חוסך התעסקות מיותרת והופך את העסק לפשוט יותר, על הטריק הזה ניתן לבסס מוצרי רשת שונים כמו IDS,IPS,WAF ועוד.

הורדת התוכנית


מקורות



יום שבת, 6 בספטמבר 2014

Arduino Yun - CGI and Python




אחרי הכרות קצרה עם Arduino Yun הגיע השלב להעמיק פנימה, המאמר מתמקד על בניית ממשק Web מתקדם בניגוד למה שהדגמתי בעבר שהיה יחסית מוגבל הפעם נעזר ב CGI ובשפת Python על מנת להשלים את המשימה בצורה פשוטה והמהירה ביותר, אבל לפני שנתחיל כדאי להצטייד במספר כלים.
  • NotePad++ - כתיבת קוד והמרות קבצים בין Windows ל Linux.
  • WinScp - העברת קבצים בין ה Yun למחשב האישי.
  • Putty - התממשקות ב SSH ל Yun.

הגדרה ראשונית

שימו לב!
  • יש לגבות את קבצי המערכת לפני התחלת העבודה!
  • יש לגבות את ספריית www.
מערכת ההפעלה OpenWrt מגיעה עם שרת Web שנקרא uhttpd שדרכו ניגשים לממשק של ה Yun, נוכל להעזר בו בשביל האתר שלנו, כל מה שצריך לעשות הוא להוריד את קובץ ההגדרות (uhttpd) שנמצא בתיקייה /etc/config/ בעזרת WinSCP ולהוסיף שורה שמייצגת את ה interpreter של Python כפי שניתן לראות בדוגמה:

# List of extension->interpreter mappings.
# Files with an associated interpreter can
# be called outside of the CGI prefix and do
# not need to be executable.
# list interpreter ".php=/usr/bin/php-cgi"
# list interpreter ".cgi=/usr/bin/perl"
list interpreter   ".py=/usr/bin/python"


קוד

הדוגמה קצת יותר מורכבת מ Hello World פשוט ומכילה 2 דפים, הדף הראשון הוא ה Login שבעזרתו נקבל קלט מהמשתמש ונעביר אותו ב Get לדף השני, במקרה שעשינו טעות בשם המשתמש או הסיסמא השרת יזריק קוד של Javascript שיחזיר את הלקוח ל Login,

על מנת לא לפגוע באתר המקומי של ה Yun נוסיף תיקייה חדשה ב www/cgi-bin בה נאכסן את קבצי ה Python אבל לא נוכל להכניס בה את קבצי ה JS או ה CSS כי השרת יחשוב שהוא צריך להריץ אותם ויחפש את interpreter המתאים, לכן יש לאכסן את שאר הקבצים מחוץ לתייקית cgi-bin.

login.py

#!/usr/bin/env python
import os

#set http header, leave double space
print "Content-type: text/html"
print

#print html content
print """
     <html>
     <head>
     <title>Login</title>
     
     <meta name="viewport" 
     content="width=device-width,initial-scale=1,maximum-scale=1" />
     
     <link href="/../example/css/jquery.mobile-1.4.3.min.css" rel="stylesheet" type="text/css" />
     <script src="/../example/js/jquery-2.1.1.min.js" type="text/javascript"></script>
     <script src="/../example/js/jquery.mobile-1.4.3.min.js" type="text/javascript"></script>
     <script src="/../example/js/jquery.validate.min.js" type="text/javascript"></script>
     </head>
     
     <body>
          <form action="/cgi-bin/example/welcome.py" method="get">
             <div style="margin-top:5%;margin-left:5%;margin-right:5%">
                Name: <input type="text" name="txb_name">  <br />
             </div>
             <div style="margin-top:5%;margin-left:5%;margin-right:5%">
                Password: <input type="password" name="txb_password" />
             </div>

             <div style="margin-top:5%;margin-left:5%;margin-right:5%">
                <input type="submit" value="Submit" />
             </div>
          </form>

     </body>
     </html>
      """

welcome.py

#!/usr/bin/env python
import os
import cgi

form = cgi.FieldStorage()

# get date from post/get
name = form.getvalue('txb_name')
password  = form.getvalue('txb_password')

# set http header, leave double space
print "Content-type: text/html"
print

# write head
print """
    <html>
    <head>
    <title>Welcome</title>

    <meta name="viewport"
        content="width=device-width,initial-scale=1,maximum-scale=1" />
        
    <link href="/../example/css/jquery.mobile-1.4.3.min.css" rel="stylesheet" type="text/css" />
    <script src="/../example/js/jquery-2.1.1.min.js" type="text/javascript"></script>
    <script src="/../example/js/jquery.mobile-1.4.3.min.js" type="text/javascript"></script>
    </head>"""

# change body depend on the request
if password != "12345" and name != "proxytype":
   print """
        <body>
            <script type="text/javascript">
               window.location.href = 'login.py';
            </script>
            Redirecting...
        </body>
        </html>"""
else:
    print """
        </head>
        <body>
        <h1 style="margin-top:5%%;margin-left:5%%">Welcome, %s !</h1>
        </body>
        """ % (name)
# % is symbol for string formatting for return it in string enter double '%%'
# printing close tag
print "</html>"


נקודות נוספות

CGI רגיש מאוד ולפעמים עלולים לקבל שגיאות שלא תמיד ניתן להבין מה בדיוק הבעיה, רכזתי מספר טיפים שיפתרו את רוב התקלות:
  • Permissions - יש לוודא שהסקריפטים של ה Python נמצאים בתיקיית cgi-bin ושיש שלהם הרשאות גבוהות ניתן להעזר בפקודה ב chmod a+x, זה יעשה את העבודה.
  • (End Of Line (EOL - יש הבדל באיך מערכות ההפעלה מתייחסות לסוף השורה, ב Windows בכל סוף שורה יש את התווים r\n\ ולעומת זאת במערכות Unix רק את התו n\ לכן במקרה שכותבים ב Windows יש לשמור את הקבצים בהגדרת Unix בעזרת Notepad++:
זה הכרחי?!

  • (Bytes Order Mark (BOM - תו Unicode שמוכנס לתחילת הקובץ מאפשר להקל על צורת הקידוד במקרה של Unicode, UTF-16 ו UTF-32 במקרה שלנו נשתמש ב UTF-8 ככה שאין לו משמעות וצריכים להוריד אותו בעזרת Notepad++: 
קידוד עולם אכזר!



תוצאה סופית



סיכום

בעזרת Python בשילוב JavaScript ו Css נוכל לבנות ממשקי ניהול מורכבים, וזה בזכות ה Linux שמאפשר לנו לעבוד עם שירותים מתקדמים יותר ונפח זיכרון גדול לקבצים , אומנם הדגמתי על Python אבל אפשר לעבוד גם עם PHP רק שהוא לא מגיע באופן טבעי עם הלוח ויש להוריד אותו בנפרד.

ללא גבולות...

יום שבת, 23 באוגוסט 2014

Arduino Yun - Starting Guide




עד עכשיו ראינו 2 סוגים של לוחות פיתוח, הראשון היה Arduino שמבוסס על מעבד פשוט ללא מערכת הפעלה, לעומתו ראינו את BeagleBone ואת Raspberry PI שמבוססים על מעבדים יותר מתוחכמים שמריצים Linux, מפתחי Arduino לא יכלו להיות אדישים והוציאו דגם חדש שמשלב בתוכו את 2 העולמות.

הוא מכיל 2 מעבדים, הראשון והמוכר ATmega 328u4 במהירות 16MHZ ו 32KB של זיכרון ,הוא אחראי על העבודה מול החיבורים הדיגטליים והאנלוגים המקוריים שיש בגרסת ה Uno הותיקה, המעבד השני הוא Atheros AR 9331 שמבוסס על ארכיטקטורת MIPS במהירות 400Mhz עם 64MB של זיכרון, הוא אחראי על שאר ה Peripherals כמו הרשת הקווית ,האלחוטית ,כרטיס הזיכרון וה USB ,הלוח מריץ מערכת OpenWRT שכתבתי עליה בעבר.

דרישות

  • Arduino Yun
  • Led
  • 330ohm Resistor
  • Wires

בגודל של Uno

הפעלה ראשונית

הלוח מגיע מוכן, מה שנשאר לעשות הוא לחבר אליו חשמל ולהתחבר ל Access Point שהוא יוצר אוטומטית, נפתח את הדפדפן ונכניס את הכתובת של ה Default Gateway שקיבלנו (במקרה שלי 192.168.201.223), דרכה נגיע לממשק הניהול.


הסיסמא ההתחלתית היא arduino

הממשק עצמו מאוד פשוט, דרכו ניתן להגדיר את התקשורת WIFI, אבל יש בו אפשרות נוספת שכדאי להכיר שנקראת Rest API , זה המנגנון דרכו נדבר עם המעבד ATmega דרך שרת Http ששולט על הפינים כפי שנראה בהמשך.

חלק קטן מ OpenWRT


הממשק הבסיסי מאוד פשוט ועונה על הצרכים למתחילים, אבל למי שרוצה להעמיק ניתן להתחבר לממשק המקורי של OpenWRT שמזכיר מאוד נתב סטנדרטי על ידי לחיצה על הקישור Advanced Configure Panel.

טירוף...


Bridge

לפני שנתחיל לכתוב את התוכנית המגשרת בין 2 המעבדים, תחילה יש להשתמש בסביבת Arduino מעודכנת מעל 1.5 שמכילה את הספריות של Yun ו Driver חדש יותר שתומך בלוח,את התוכנית צורבים על הזיכרון של המעבד ATmega בצורה סטנדרטית כמו שעושים לשאר הלוחות של Arduino, רק חשוב לבחור את הלוח המתאים ברשימה, קיימת דוגמה מוכנה שמגיעה עם הגרסה המעודכנת ולמי שחסר ניתן להוריד אותה מכאן כך שלא צריך להתאמץ יותר מידי.

בעזרת פרטוקול Http ושרת Web, נוכל לשלוט על הפינים בעזרת מבנה Url, לדוגמה נניח שנרצה להפוך את Pin 13 ל OutPut נקרא לכתובת במבנה מסויים שברגע שתגיע לשרת הוא יחתוך אותה ויפעל בהתאם כפי שניתן לראות בדוגמאות הבאות:

הגדרת הרגל כ Input / Output:
http://arduino.local/arduino/mode/13/output
כתיבת ערך לרגל דיגיטלית:
http://arduino.local/arduino/digital/13/1
קריאת ערך מרגל דיגיטלית:
http://arduino.local/arduino/digital/13
כתיבה ערך לרגל אנלוגית:
http://arduino.local/arduino/analog/2/128
קריאת ערך מרגל אנלוגית:
http://arduino.local/arduino/analog/2


אחרי שצרבנו את התוכנית, אפשר לעבור לתוכנית השניה שנכתוב Python שתדבר עם ה Bridge ותשלוט ברגליים על פי הצורך, במקרה הזה נתחבר בצורה מקומית, זה יתרון אדיר שמאפשר לנו לשלב את המעבדים בצורה פשוטה ולהנות מכל העולמות בו זמנית, נשתמש במספר כלים שיעזרו לנו בעבודה:
  • Putty - נתחבר ל Arduino Yun בעזרת SSH.
  • WinSCP - העברת קבצים בין המחשב האישי ל Arduino Yun.
נפתח Notepad ונרשום את הקוד הבא:

#Arduino Yun blink example using Bridge with Python
#proxytype.blogspot.com
#getting Thread function from Thread class
from threading import Thread

#add Libraries
import urllib2
import time

#selected Pins
PIN = 7

#set pins output/input
response = urllib2.urlopen('http://localhost/arduino/mode/' + str(PIN) + '/output')
print response.read()

def main(n):
    #flag
    Status = False
    while True:
if Status == False:
Status = True
response = urllib2.urlopen('http://localhost/arduino/digital/' + str(PIN) + '/1')
print response.read()
else:
Status = False
response = urllib2.urlopen('http://localhost/arduino/digital/' + str(PIN) + '/0')
print response.read()
time.sleep(n)
#start in new thread, send second to sleep
t = Thread(target=main, args=(1,))
t.start()



נשמור את הקובץ ונעביר אותו ל Yun בעזרת WinSCP, ואח"כ נתחבר ל Terminal בעזרת SSH ונריץ את התוכנית:



סרט לדוגמה



סיכום

הלוח הזה לוקח את כל העסק צעד אחד קדימה במחיר יחסית שפוי של 60$, ומאפשר לנו לכתוב פרוייקטים יותר מורכבים ועדיין להישאר באותה מסגרת נוחה של Arduino, בהמשך נראה כיצד להשתמש בספריות היעודיות של ה Yun שמאפשרות לגשת למשאבי ה Linux מבלי לעזוב את ה IDE.

יותר שכל באותו מקום...


יום שבת, 16 באוגוסט 2014

Raspberry Pi - Starting Guide



אם חשבתם ש Raspberry PI יעבור לידי ואני לא אתייחס אליו אז טעיתם, הלוח הזה שינה את כללי המשחק בכל הקשור למערכות Embedded ו SOC, הוא זול ,פשוט ומגיע עם מגוון רחב של חיבורים, אין משהו שלא עשו איתו ממערכות פשוטות למחשבי על, הוא תוכנן ע"י ארגון צדקה בשם Raspberry Pi Foundation שמטרתו ללמד מדעי המחשב באופן חופשי בעולם.

הסדרה מכילה 3 דגמים עם את אותו מעבד ARM 700MHZ (בנתיים) וההבדלים העיקריים הם בציוד היקפי (Peripherals) שמשתנים מדגם לדגם ומשפיעים על המחיר.

www.sparkfun.com



דרישות

  • Raspberry PI Model B
  • SDCard 8 GB
  • TFT Monitor
  • Keyboard + Mouse
  • LED
  • 330ohm Resistor
  • Breadboard
  • Wires




שימו לב!
  • יש לחבר לספק 5V בלבד!
  • חשוב לזכור שהפינים של ה 5V מוגבלים עד 300ma וה 3.3V עד 50ma.

התקנה

יש להוריד את קבצי ההתקנה של מערכת ההפעלה מהאתר הרשמי, קיימות גרסאות שונות של Linux שניתן להתקין, יש גרסת Noobs למשתמשים חדשים שמאפשרת התקנה מהירה ונוחה.

http://www.raspberrypi.org/downloads/


לפני ההתקנה של מערכת ההפעלה יש לוודא שהכרטיס ה SD עבר פורמט, ניתן לבצע זאת בעזרת SD Formatter שבהורדה חינם מהאתר של SDCard, או באמצעות הפורמט של Windows אבל בחלק מהכרטיסים זה עלול לגרום לאיטיות ולבעיות.

https://www.sdcard.org/downloads/formatter_4/

לאחר סיום הפורמט נפתח את קובץ ה Zip ונעתיק את התוכן שלו ל SDcard, נחבר מקלדת ומסך ואפשר לומר שסיימנו, הלוח מגיע עם כניסת HDMI שניתן לחבר לכל טלויזיה או מסך וחיבור S-Video סטנדרטי למסכים קצת יותר פרימיטביים לצורך הדוגמה אני משתמש במסך בגודל 5 אינץ שמתאים בעיקר לרכב שרכשתי מ DX תמורת 40$ היתרון שלו על פני שאר המסכים שניתן גם לחבר VGA, בדר"כ המסכים האלה מגיעים ללא ספק כוח ודורשים 12V אבל הצלחתי להפעיל אותו ב 9V ואני מאמין שאפשר גם בפחות.

סיני זול שעושה את עבודה.


במהלך ה Boot של הלוח ניתן ללחוץ Shift 3 על מנת להעביר את התצוגה ל S-Video ב PAL ומשם להמשיך בהתקנה, קובץ ההתקנה מכיל מספר distributions של Linux מותאמות ל Raspberry Pi, אבל מומלץ להשתמש ב Raspbian שהיא הפופלרית ביותר.

שתו קפה יש זמן...

בסיום ההתקנה הלוח יעשה Boot ויעלה מסך הגדרות הם מספר אפשרויות, קודם כל מומלץ להחליף את סיסמאת ה Default שהיא raspberry עבור משתמש PI, ניתן להגדיר Overclock ועוד כל מיני דברים, תמיד אפשר לחזור למסך הזה ע"י הפקודה raspi-config עם Sudo.


Overclock - נחמד!

בסיום יעלה לנו הטרמינל הרגיל של Linux, בעזרת הפקודה startx נעבור לממשק חלונאי:

ואוו...

קוד

המערכת מוכנה ואפשר להתחיל לכתוב את הקוד הראשון, בעבר הדגמתי כיצד לתכנת פינים ב BeagleBone וזה לא כלכך שונה מה PI, כי נחזור על אותה קלישאה ישנה שהכל ב Linux זה קובץ וכל מה שנשאר זה לפתוח אותו ולכתוב בו ערך באיזה שפה שנרצה, במקרה של ה PI שפת ה Python מגיעה כחלק מהמערכת אבל לפני הקוד חשוב להכיר את הפינים עצמם.

26 פינים שחלקם עם שימוש נוסף

26 פינים שבחלקם ניתן לעשות יותר משימוש אחד, ניתן לעבוד עם מספר ממשקים שונים (UART,SPI,I2C), במערכת מגיעה סביבת פיתוח פשוטה של Python שנקרא IDLE שבעזרתו נכתוב את הקוד, אם לא בא לכם לעבוד ישירות על הלוח ניתן להתחבר אליו בעזרת SSH.


#add Libraries
import RPi.GPIO as GPIO

#use board pins
GPIO.setmode(GPIO.BOARD)
#selected pin
pin = 7
#set GPIO as output
GPIO.setup(pin, GPIO.OUT)

#set flag false
status = False

while True:
if status == False:
GPIO.output(pin,GPIO.LOW)
status = True
else:
GPIO.output(pin,GPIO.HIGH)
status = False
x = raw_input('Press Key!')





מבנה סופי



סרט הדגמה




סיכום

הלוח הזה פשוט מדהים, אפשר לעשות איתו הכל ויש סביבו קהילה ענקית של מפתחים ,כל יום נולד בעזרתו פרוייקט חדש, הוא גרם לירידת מחירים רצינית בתחום וקבע רף חדש של 40$ ללוח, בקיצור מדובר על מחשב קטן שמתאים לצרכים בסיסיים ואפילו מעבר לכך.

יש פטל?







יום שבת, 26 באפריל 2014

Linked List Linux Kernel


בהמשך למסע ב Kernel החלטתי להתמקד בקצרה על רשימות מקושרות, למי שלא מכיר או שהספיק לשכוח מדובר על דרך לחבר בין אובייקטים בעזרת מצביעים לאובייקטים אחרים בזיכרון, לכל אובייקט יש מצביע לאובייקט שלפניו ומצביע לאובייקט שאחריו כפי שניתן לראות בתמונה:



שימו לב!

  • עבודה מול ה Kernel עלולה להזיק למחשב שלך, מומלץ לעבוד על תחנה וירטואלית.
  • המאמר נכתב על Fedora 14.

מערכת ההפעלה נותנת כלים פנימיים שעוזרים לנהל את הרשימות במקום שנהל בעצמנו, קובץ list.h חושף מספר פונקציות ו Macros כפי שניתן לראות בדוגמה:


myList.c

//define module
#include <linux/module.h>
//we are in the kerenl
#include <linux/kernel.h>
//linked list header
#include <linux/list.h>

//example struct to be linked
struct element {
int id;
char name[15];
char description[50];

//creating header, next and previous pointers
struct list_head list;
};

//the list!
struct element elementsList;

void addElements(void)
{
struct element *tmp;

int listMax = 20;
int i = 0;

for(i = 0; i < listMax; i++)
{
//getting memory for the struct
tmp = kmalloc(sizeof(*tmp),GFP_KERNEL);
tmp->id = i;
strcpy(tmp->name, "Element");
strcpy(tmp->description, "just simple element inside the list");

//initializes pointers
INIT_LIST_HEAD(&tmp->list);
//add the element at the end of the list
  list_add_tail(&(tmp->list), &(elementsList.list));

}

printk(KERN_INFO "load complete\n");
}

void deleteElements(void)
{
struct element *tmp, *node;
  //if deleting elements better using this function,
  //it preserve the pointer to the next element,preventing holes inside the list. 
  list_for_each_entry_safe(node, tmp, &elementsList.list, list){
         //remove from the list
         list_del(&node->list);
         //release the memory
         kfree(node);
    }
}

void viewElements(void)
{
struct element *tmp;

//running through all elements
list_for_each_entry(tmp, &elementsList.list, list) {
printk(KERN_INFO "Element\n ID: %d; Name: %s; 
                         Description: %s\n", tmp->id, tmp->name, tmp->description);
}
}

int c_module() {

//initializes the list
INIT_LIST_HEAD(&elementsList.list);
printk(KERN_INFO "initialize list complete\n");
addElements();
viewElements();
return 0;

}

void r_module()
{
printk(KERN_INFO "delete list\n");
deleteElements();
printk(KERN_INFO "remove module complete\n");
}

module_init(c_module);
module_exit(r_module);



מגדירים מבנה עם אובייקט מיוחד מסוג list_head שמכיל בתוכו את המצביעים, מייצרים מופע כללי של המבנה שמייצג את ראש הרשימה, בשלב טעינת ה Module  נטענת הפונקציה addElements שמכניסה 20 אובייקטים לרשימה, היא מאפסת את המצביעים ושולחת כל מבנה לפונקציה (list_add_tail) שמוסיפה אותו לסוף הרשימה.

לסיום רצה הפונקציה viewElements שמציגה את הרשימה בעזרת המאקרו list_for_each_entry שדרכו ניתן לעבור על האלמנטים, בהסרת ה Module רצה הפונקציה deleteElements שמפעילה את המאקרו list_for_each_entry_safe שדואג להסרה בטוחה של אובייקטים מבלי שהיו חורים ברשימה.

סיכום

מדובר בכלי חזק אבל קצת מסורבל שעלול לגרום לכאב ראש לא קטן אם ננסה לממש אותו בעצמנו, Linux חוסכת את כל ההתעסקות הכואבת במצביעים ונותנת תשתית נוחה לבניה ולניהול רשימות מקושרות.

מידע נוסף


רשמת?




יום שבת, 12 באפריל 2014

Procsfs Linux Kernel



מערכת Linux נותנת מספר דרכים להעברת מידע בין ה User Space ל Kernel, אחת הדרכים הצגתי במאמר הפתיחה בעזרת Character Device שיוצר קובץ בתיקיית ה Dev דרכו מדברים עם ה Module, השיטה הנוספת שהחלטתי להתמקד עליה היא Procs File System שמאוד דומה רק שהקובץ ממוקם בספריית ה Proc ללא צורך רישום כ Device.

הקובץ פותח שער שדרכו כותבים וקוראים מה Module בדומה לעבודה על קבצים (פתיחה,כתיבה,קריאה וסגירה) וכך נוצר ממשק אחיד, פשוט ונוח, לצורך הדוגמה כשנריץ את הפקודה Cat על הקובץ cpuinfo בתקיית proc נקבל את הפלט הבא:

[root@localhost proc]# cat cpuinfo 
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 26
model name : Intel(R) Core(TM) i7 CPU         950  @ 3.07GHz
stepping : 5
cpu MHz : 3158.658
cache size : 6144 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 5
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx lm constant_tsc up pni monitor ssse3 lahf_lm
bogomips : 6317.31
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:

שימו לב!
  • עבודה מול ה Kernel עלולה להזיק למחשב שלך ממולץ לעבוד על תחנה וירטואלית.
  • המאמר נכתב על Fedora 14.

מייצרים אובייקט מסוג proc_dir_entry שמייצג את הקובץ בתיקיית ה Proc, לאחר מכן דורסים את הפונקציות Read ו Write של הקובץ לפונקציות פנימיות ב Module, מגדירים Buffer עבור המידע שיעבור ואפשר להגיד שסיימנו.

procModule.c

//define module.
#include <linux/module.h>
//we are in the kernel.
#include <linux/kernel.h>
//proc header.
#include <linux/proc_fs.h>
//copy from user.
#include <asm/uaccess.h>

#define PROCMAXSIZE 4096
#define PROC_NAME "Bridge"

//pointer to the proc file.
static struct proc_dir_entry *procfile;

//buffer of the proc file.
static char procbuffer[PROCMAXSIZE];

//buffer size.
static unsigned long buffersize = 0;

//proc file new read function will overwrite the old one.
//buffer: the kernel buffer pointer.
//buffer_location: the starting point of reading
//offset:the offset from the beginning of the file.
//buffer_length: the number of chars to read.
//eof: end of file pointer if there is more to read.
//data: private data.
//return the read length value.
int procfile_read(char *buffer, char **buffer_location,off_t offset, int buffer_length, int *eof, void *data)
{
int length;

if (offset > 0) {
//nothing to read reset length read value.
length  = 0;
} else {

//copy data from kernel space to user space.
memcpy(buffer, procbuffer, buffersize);

printk(KERN_INFO "Read: %s \n", procbuffer);

//set the length read value.
length = buffersize;
}
return length;
}

//proc file new write function will overwrite the old one.
//file: open file structure.
//buffer: user space buffer.
//count: the number of chars been writes.
//data: private data.
//return the read length value.
int procfile_write(struct file *file, const char *buffer, unsigned long count, void *data)
{
//getting buffer size.
buffersize = count;

//check for overflow.
if (buffersize > PROCMAXSIZE ) {
buffersize = PROCMAXSIZE;
}

//copy data from user space buffer to kernel space buffer.
if ( copy_from_user(procbuffer, buffer, buffersize) ) {
//return error if unable.
return -EFAULT;
}

printk(KERN_INFO "Write: %s \n", procbuffer);

//return the size of writing.
return buffersize;
}


int c_module()
{
//create the proc file on module creation,
//give name and permissions.
procfile = create_proc_entry(PROC_NAME, 0644, NULL);

//if null return error handling.
if (procfile == NULL) {

remove_proc_entry(PROC_NAME, NULL);
printk(KERN_ALERT "Cannot create Proc: %s\n",PROC_NAME);
//maybe cause by out of memory.
return -ENOMEM;
}

//overwrite the original read and write functions of the file.
procfile->read_proc  = procfile_read;
procfile->write_proc = procfile_write;

printk(KERN_INFO "%s created\n", PROC_NAME);

//operation complete.
return 0;
}


void r_module()
{
//remove proc file entry from folder.
remove_proc_entry(PROC_NAME, NULL);
printk(KERN_INFO "/proc/%s removed\n", PROC_NAME);
}


module_init(c_module);
module_exit(r_module);


נעשה Compile בעזרת Makefile ונטען את ה Module לזיכרון עם הפקודה insmod, נבדוק בתיקיית ה proc שנוצר הקובץ בשם Bridge ונתחיל לדבר איתו בעזרת פקודות מה Command Line כפי שניתן לראות בדוגמה:

[root@localhost proc]# echo "send data to module" >> /proc/Bridge 
[root@localhost proc]# cat Bridge 
send data to module


סיכום

ראינו מספר דרכים לדבר עם ה Kernel בעזרת קבצים, הם מחולקים לתיקיות במערכת כמו Proc עבור ה Processes או Dev עבור Devices, יש דרכים נוספות כמו Sysfs, Debugfs ו Configfs שקצת יותר מורכבות אבל עובדות על אותו עיקרון במטרה לתת תשתית להצגה והעברת מידע נוחה ללא צורך בבנייה של ממשקים מורכבים.

בהצלחה...