Thursday, December 17, 2009

SLIP driver

;----------------------------------------------------------------------------
;                       SLIP COMMUNICATION HANDLER
;                           Intel 386-based
;                      Receiver and Transmitter ISR
;
;                           * Thesis Project *
;                        M. Lutfi Shahab 13389011
;                   Department of Engineering Physics
;                       Institut Teknologi Bandung
;                           (c) Bandung, 1995
;
;                        First made:  March 1995
;                        Last upated: 4-Jan 1996
;----------------------------------------------------------------------------
;
;
; 26/7/95       I found a bug on ISR. ISR should push flags to stack, but
;               in older version, this didn't!
;               I observed this bug affected keyboard flag checking
; 11 Nov 1995   In next version I plan to use extended USART controller,
;                                        like Intel 82510 and 16550
;               Detecting these chips done with writting appropriate setting
;                                        code to FCR (FIFO control register) and detects their presences
;                                        in extended fields of IIR (Interrupt Control Register).
;  8 Jan 1995   16 bit Cyclic Redundancy Check added.
;
;-----------------------------------------------------------------------------
;IFDEF TASM
PAGE 60, 120
;ENDIF

MODEL large, C

MASM

DEBUG      EQU     0       ; debug

INCLUDE PIC.ASH
INCLUDE SLIP.ASH

.DATA
EXTRN       base           :WORD
EXTRN       queue          :tQUEUE
EXTRN       baudrate       :DWORD
EXTRN       int_no         :BYTE
EXTRN       thr            :WORD
EXTRN       rdr            :WORD         ; base
EXTRN       ier            :WORD         ; = base+1
EXTRN       iir            :WORD         ; = base+2
EXTRN       lcr            :WORD         ; = base+3
EXTRN       mcr            :WORD         ; = base+4
EXTRN       lsr            :WORD         ;
EXTRN       msr            :WORD         ; = base+6
EXTRN       scp            :WORD         ; scratch pad = base + 7
EXTRN       installed      :WORD         ;  initialization flag
EXTRN       imr            :WORD
EXTRN       rcv            :tCOMBUF

packet_sem       DW     0
xmit_time        DW     200    ;timeout
rcv_time         DW     200
timeout          DW     200
recv_pkt_ready   DW     0

IF DEBUG EQ 1
        isr_msg                  DB      'Ini di dalam recv_isr', 10, 13, '$'
        isr_in_sema              db      'Semaphore > 0', 10,13,'$'
ENDIF

.CODE

; This variable must be put in CODE SEGMENT
in_recv_isr      DW    0
IF DEBUG EQ 1
;-------------------------
; al = charactrer to print
;-------------------------
print PROC NEAR
      PUSH      ax bx cx dx ds es si di bp ; Turbo Assembler's macro
      mov       bh, 0
      mov       bl, 07h
      mov       ah, 0eh
      int       10H
      POP       bp di si es ds dx cx bx ax
      RETN
print ENDP

;---------------------
; dx => offset string
;---------------------
print_msg PROC
      PUSH      ax bx cx dx ds es si di bp
      ASSUME    ds:@data
      mov       ax, @data
      mov       ds, ax
      mov       ah, 9h
      int       10H
      POP       bp di si es ds dx cx bx ax
      RET
print_msg ENDP
ENDIF


xmit  PROC    NEAR
      retn
xmit  ENDP


;--------------------
send_char   PROC NEAR
      PUSH      ds dx cx
      xchg      ah,al           ; put data char into ah
      push      ax
      ASSUME    ds:@data
      mov       ax,@data        ; resetting ds to DGROUP
      mov       ds, ax
      pop       ax
      xor       cx,cx           ; 64K retry counter

 sendch1:
      mov       dx, [lsr]
      in        al, dx
      jmp       $+2             ; use time, prevent overdriving UART
      jmp       $+2
      test      al, LSR_THRE    ; Transmitter (THRE) ready?
      jnz       sendch2         ; nz = yes
      loop      sendch1
      stc                       ; carry set for failure
      jmp       short sendch3   ; timeout

 sendch2:
      xchg      al, ah          ; now send it
      mov       dx, [thr]
      out       dx, al          ; send the byte
      jmp       $+2             ; use a little time
      clc                       ; status of success

 sendch3:
      POP       cx dx ds
      RETN
send_char       ENDP


; ------------------------------------------
; int _Send_SLIP_Packet(tSLIP_Pkt *pkt)
; ------------------------------------------
PUBLIC C _Send_SLIP_Packet
_Send_SLIP_Packet PROC C pkt:DWORD

;--------- Turbo Assembler automatically set stack frame
        PUSH      ds si
        sti                          ; enable interrupts
        lds       si, pkt

;-------- MODIFIED because CRC16 addition ($LTF$, 8/JAN/95)
        ;mov       cl, ds:[si].datalen
        ;xor       ch, ch
        ;cmp       cx, DATASIZE
        ;jle       LESS_OR_EQU
        ;mov       cx, DATASIZE        ; align to DATASIZE
        mov       cx, PKTSIZE

LESS_OR_EQU:
  ;---add       cx, 4               ; total packet length =
                                                                                                                                ; sizeof(pktlen)+sizeof(datatype)+sizeof(crc)
                                                                                                                                ; = 1 + 1 + 2 = 4 bytes
        cld
        mov       al, FR_END          ; Flush out any line garbage
      call      send_char
      jc        send_pkt_end        ; c = failure to send

        ; Copy input to output, escaping special characters
 send_pkt_1:
      lodsb
      cmp       al, FR_ESC          ;escape FR_ESC with FR_ESC and T_FR_ESC
      jne       send_pkt_2
      mov       al, FR_ESC
      call      send_char
      jc        send_pkt_end
      mov       al, T_FR_ESC
      jmp       short send_pkt_3

 send_pkt_2:
      cmp       al, FR_END          ;escape FR_END with FR_ESC and T_FR_END
      jne       send_pkt_3
      mov       al, FR_ESC
      call      send_char
      jc        send_pkt_end
      mov       al, T_FR_END

 send_pkt_3:
      call      send_char
      jc        send_pkt_end
      loop      send_pkt_1              ; do cx user characters
      mov       al, FR_END              ; terminate it with a FR_END
      call      send_char
      jc        send_pkt_end
      mov       ax, 1                   ; success
      jmp       short keluar

 send_pkt_end:
      xor       ax, ax                  ; error occured
 keluar:
      POP       si  ds
      RET
_Send_SLIP_Packet       ENDP


;------------------------------------
;  static void near asyrxint(void)
;  return:
;    -  packet_sem
;    -  recv_pkt_ready
;------------------------------------
asyrxint        PROC NEAR
      push      ds               ; save ds register
      ASSUME    ds:@data
      mov       ax, @data        ; ds points to DGROUP !
      mov       ds, ax
                mov       ax, WORD PTR [rcv.buf_end+2] ; just check !!
      xor       bx, bx
      cmp       WORD PTR [baudrate+2], 0
      jb        short asyrxint_a
      jbe       check_again
      jmp       short asyrxint_a

 check_again:
      cmp       WORD PTR [baudrate], 9600      ;below 9600 we're strictly
      jb        asyrxint_a                     ;interrupt driven
      mov       bx, rcv_time

 asyrxint_a:
                les       di, DWORD PTR [rcv.head]
      xor       bp, bp                  ; set flag to indicate 1st char processed
      mov       si, packet_sem          ; optimization
      mov       ah, LSR_DR

 asyrxint_again:
      xor       cx, cx                  ; initialize counter
      mov       dx, [lsr]

 asyrxint_in:
      in        al, dx                 ; check for data ready
      test      al, LSR_DR
      jnz       asyrxint_gotit         ; yes - break out of loop
      inc       cx                     ; no - increase loop counter
      cmp       cx, bx                 ; timeout?
      jae       asyrxint_exit          ; yes - leave
      jmp       asyrxint_in            ; no - keep looping

 asyrxint_gotit:
      mov       dx, [rdr]
      in        al, dx

; Process incoming data;
; If buffer is full, we have no choice but to drop the character
;
; segment of rcv.head and rcv.tail is same !!
; so just compare their offsets

      cmp      di, WORD PTR [rcv.tail]; is it buffer collision ?
      jne      asyrxint_ok             ; none - continue
      or       si, si                  ; maybe - if there are packets
      jnz      asyrxint_exit           ; yes exit

 asyrxint_ok:
      stosb
      cmp      di, WORD PTR [rcv.buf_end]  ; did we hit the end of the buffer?
      jne      asyrxint_3                  ; no.
      mov      di, WORD PTR [rcv.buf]      ; yes - wrap around.

 asyrxint_3:
      cmp      al,FR_END            ; might this be the end of a frame?
      jne      asyrxint_reset       ; no - reset flag and loop
      inc      si                   ; yes - indicate packet ready
      cmp      si, 1                ; determine if semaphore is <> 1
      jne      asyrxint_chk_flg     ; yes - recv_frame must be active
      inc      recv_pkt_ready       ; no - set flag to start recv_frame
  IF DEBUG EQ 1
      mov     al, 'O'
      call    print
  ENDIF

 asyrxint_chk_flg:
      cmp      bp, 0                ; was this the first char?
      jne      asyrxint_1           ; no - exit handler

asyrxint_reset:
      inc      bp                   ; set 1st character flag
      jmp      asyrxint_again       ; get another character

asyrxint_exit:
asyrxint_1:
      mov      WORD PTR [rcv.head], di     ; set offset pointed by rcv.head
      mov      [packet_sem], si

exit:
      pop      ds
      RETN
asyrxint        ENDP


;--------------------
; registers used:
;    ax, ds
;
;--------------------
recv    PROC NEAR
 recv_2:
      push     ds
      mov      ax, @data
      mov      ds, ax
      mov      dx, [iir]
      pop      ds
      in       al, dx          ;any interrupts at all?
      test     al, IIR_IP
      jne      recv_1          ;no.
      and      al,IIR_ID
      cmp      al,IIR_RDA      ;Receiver interrupt
      jne      recv_3
      call     asyrxint
      jmp      recv_2

 recv_3:
 recv_5:
        ; process IIR_MSTAT here.
        ; If CTS and packet ready then
        ;               enable the      transmit buffer empty interrupt
        ; else
        ;         disable the transmit buffer empty interrupt
        ;
      cmp      al, IIR_MSTAT
      jne      recv_1
      push     ds
      mov      ax, @data
      mov      ds, ax
      mov      dx, [msr]
      pop      ds
      in       al, dx
      test     al, MSR_CTS         ; is CTS bit set
      jz       recv_5_1            ; no - disable xmit buffer empty int
      jmp      recv_2

 recv_5_1:
      jmp      recv_2
        ;process IIR_RLS here
 recv_1:
      RETN
recv  ENDP


;-------------------------------------------
; recv_char
;    si = receive buffer = FP_OFF(rcv.tail)
;    bx = receive count
;    ds = data segment
; return:
;    al = next char
;    bx = pointer rcv.head
; zero flag:
;    nz (0) : character received
;    zr (1) : no more chars in this frame
;-----------------------------------------
recv_char       PROC NEAR
      push     ds                      ; we assume that data segment has been set
      mov      ax, WORD PTR [rcv.tail+2]
      mov      ds, ax
      lodsb
      pop      ds
      cmp      si, WORD PTR [rcv.buf_end]
      jne      recv_char_1
      mov      si, WORD PTR [rcv.buf]

 recv_char_1:
      mov      bx, WORD PTR [rcv.head]
      cmp      si, bx                      ; if ((rcv.tail==rcv.head)|| //buffer collision
      je       recv_char_2                 ;       (_AL==FR_END))
      cmp      al, FR_END                  ; return (1);
                                           ; else return (0);
 recv_char_2:
      RETN
recv_char   ENDP

;------------------------------
; recv_copy
; on entry:
;    ds:si = packet_buffer
;    cx = packet length
; do copy packet_to queue.write
;------------------------------
recv_copy PROC NEAR
      les      di, DWORD PTR [queue.write]
      mov      ax, es
      or       ax, di
      jz       fail
  ulang:
      rep      movsb
      mov      WORD PTR [queue.write], di  ; points to next entry list
  fail:
      RETN
recv_copy ENDP

;---------------------------------------
; INTERRUPT SERVICE ROUTINE
;   almost all registers used, so
;   we must save all of those.
;   use register variables as possible
;   because accesses to memory variable
;   consuming much more time
;----------------------------------------
.CODE

PUBLIC C dummy_isr
dummy_isr PROC FAR
      PUSH     ax bx cx dx ds es si di bp
      pushf
      ASSUME   ds:@data
      mov      ax, @data
      mov      ds, ax
      mov      al, [int_no]               ;get hardware int #
      add      al, TMREOI                 ; make specific EOI dismissal
      out      BASE8259, al
      popf
      POP      bp di si es ds dx cx bx ax
      iret
ENDP


PUBLIC  C recv_isr
recv_isr   PROC FAR
      ;jmp      short start
      ;in_recv_isr     DW    0

  start:
      cmp      cs:[in_recv_isr], 0
      je       no_re_enter
      iret

no_re_enter:
      PUSH     ax bx cx dx ds es si di bp
      pushf
      mov      cs:in_recv_isr, 1
      ASSUME   ds:@data
      mov      ax, @data
      mov      ds, ax
      mov      al, int_no               ; Disable further specific device interrupt
      call     maskint

COMMENT *
 I realize this re-entrancy trap is not perfect, you could be re-entered
 anytime before the above instruction. However since the stacks have not
 been swapped re-entrancy here will only appear to be a spurious interrupt.

*
;----------------------- CALL TRANSMITTER -----------------------
      call     xmit
;----------------------------------------------------------------

COMMENT *
; The following comment is wrong in that we now do a specific EOI command,
; and because we don't enable interrupts (even though we should).
;
; Chips & Technologies 8259 clone chip seems to be very broken.  If you
; send it a Non Specific EOI command, it clears all In Service Register
; bits instead of just the one with the highest priority (as the Intel
; chip does and clones should do).  This bug causes our interrupt
; routine to be reentered if:
;       1. we reenable processor interrupts;
;       2. we reenable device interrupts;
;       3. a timer or other higher priority device interrupt now comes in;
;       4. the new interrupting device uses a Non Specific EOI;
;       5. our device interrupts again.
; Because of this bug, we now completely mask our interrupts around
; the call    to "recv", the real device interrupt handler.  This allows us
; to send an EOI instruction to the 8259 early, before we actually
; reenable device interrupts.  Since the interrupt is masked, we
; are still guaranteed not to get another interrupt from our device
; until the interrupt handler returns.  This has another benefit:
; we now no longer prevent other devices from interrupting while our
; interrupt handler is running.  This is especially useful if we have
; other (multiple) packet drivers trying to do low-latency transmits.
;
; The following is from Bill Rust, <wjr@ftp.com>
; this code dismisses the interrupt at the 8259. if the interrupt number
; is > 8 then it requires fondling two PICs instead of just one.
*

      mov      al, [int_no]              ;get hardware int #
      cmp      al, BASEVEC               ; see if its on secondary PIC
      jg       recv_isr_4
      add      al, TMREOI                ; make specific EOI dismissal
      out      BASE8259, al
      jmp      short recv_isr_3          ; all done

recv_isr_4:
      add      al, 060H - BASEVEC        ; make specific EOI (# between 9 & 15).
      out      0a0H, al                  ; Secondary 8259 (PC/AT only)
      mov      al, 062H                  ; Acknowledge on primary 8259.
      out      BASE8259, al

recv_isr_3:

;-------- save packet data to buffer
      call     recv

      cmp      [recv_pkt_ready], 1       ; is a packet ready?
      je       ready                     ; yes, start read
      jmp      recv_exit                 ; no - skip to end

  ready:
      mov      [recv_pkt_ready],  0      ; reset flag
      sti

;===================== BEGIN DEPACKETIZE DATA HERE ======================
  recv_frame:
      cmp      [packet_sem], 0         ; should we do this?
      jne      sema                    ; yes - process it
      jmp      next_sema               ; no (packet_sem==0) => exit

  sema:
      mov      bx, WORD PTR [queue.last]
      cmp      WORD PTR [queue.write], bx
      jne      not_full
 ;-------
 ; if queue.write reach queue.last, we have no choice thus just return
 ;-------
  recv_frame_full:
      mov      [queue.status], QFULL

  next_pkt:
      mov      [recv_pkt_ready], 1
      jmp      recv_exit

  not_full:
      mov      si, WORD PTR [rcv.tail]          ; get tail pointer
      xor      cx, cx                           ; count up the size here.

 ;---------- debug only, not needed int final linking
         IF DEBUG EQ 1
                  mov        bx, ds
                  mov        dx, WORD PTR [rcv.tail+2]
                  mov        ds, dx
                  mov        ax, ds:[si]
                  mov        ds, bx
                  mov        al, 'A'
                  call       print
                  ENDIF
  ;--------
  ;---- find how long message in the packet is
  recv_frame_1:
      call     recv_char                  ; get a char.
      je       recv_frame_2               ; go if no more chars (al==FR_END)
      cmp      al,FR_ESC                  ; an escape?
      je       recv_frame_1               ; yes - don't count this char.
      inc      cx                         ; no - count this one.
      jmp      recv_frame_1
 ;--- now cx contains message length

 recv_frame_2:
      jcxz     recv_frame_3                   ;no data in packet? yes - free the frame
      les      di, DWORD PTR [queue.write]
      mov      ax, es
      or       ax, di
      jnz      trail_recv
      xor      cx, cx                 ; if (queue.write==NULL)
      jmp      recv_frame_3           ; queue.write[0] = 0 //force packet length to zero
                                      ; cx reset to avoid putting a non-packet message
                                      ; into queue
 trail_recv:
      mov      bp, di                 ;remember first entry of queue line
      cmp      di, WORD PTR [queue.lastline]
      jg       recv_frame_full

; es:di -> packet buffer to be filled in
; cx = packet length
      cmp      cx, Q_WIDTH            ;if (cx > Q_WIDTH)
      jle      recv_frame_ok          ;
      mov      cx, Q_WIDTH            ;align packet to proper width, 1 byte for size
                                      ;so will truncate char beyond the bound
 recv_frame_ok:
      mov      si, WORD PTR [rcv.tail]         ;resetting pointer to rcv.tail.
      ;----mov      ax, cx
      ;----stosb                               ;queue.write[0] = cx
      mov      dx, 0                           ;count

 recv_frame_4:
      cmp      di, WORD PTR [queue.last]     ; is it reach the end of queue ?
      jne      recv_frame_ready

 recv_too_big:
      mov      [queue.status], QTOOBIG

      ;---  We don't need to store frame width anymore to simplify queue.
        ;---- The packet now contains data length to inform other task
      ;---- that frame has.
      ;----- (LUTFI, 16 Aug-95)

      ;-----mov      BYTE PTR es:[bp], Q_WIDTH     ;truncate packet
      IF DEBUG EQ 1
                mov     al, 'M'
                call print
                ENDIF
      ;jmp     next_sema
      jmp      next_pkt

 recv_frame_ready:
      call     recv_char                 ; if ((recv.tail==recv.buf_end)||(al==FR_END))
      je       recv_frame_6              ;     we're all done.
      cmp      al,FR_ESC                 ;an escape?
      jne      recv_frame_5              ;no - just store it.
      call     recv_char                 ;get the next character.
      je       recv_frame_6
      cmp      al,T_FR_ESC
      mov      al,FR_ESC                 ;assume T_FR_ESC
      je       recv_frame_5              ;yup, that's it - store FR_ESC
      mov      al,FR_END                 ;nope, store FR_END

 recv_frame_5:
      cmp      dx, cx
      jge      recv_frame_6              ; beyond queue width, so break loop
      stosb                              ;store the byte.
      inc      dx                        ;bytes stored
      jmp      recv_frame_4

 recv_frame_6:
      mov      WORD PTR [rcv.tail], si   ;we're skipped to the end
      mov      [queue.status], QOK       ;sign queue status to OK state
      jmp      recv_frame_end

 recv_frame_3:
      mov      WORD PTR [rcv.tail], si        ;set to new location

 recv_frame_end:
      jcxz     next_sema                     ; if no message, just skipped out

 kecil:
      add      bp, Q_WIDTH
      mov      WORD PTR queue.write, bp     ;next line of queue.

 next_sema:
      dec      [packet_sem]
      cmp      [packet_sem], 0                 ; are there more packets ready?
      ja       any_packet                      ; if (semaphore > 0)
      jmp      short recv_exit

 any_packet:
      jmp      recv_frame                      ; yes - execute again

 recv_exit:

;         DDP - This is a BIG mistake.  This routine SHOULD NOT enable interrupts.
;         doing so can cause interrupt recursion and blow your stack.
;         Processor interrupts SHOULD NOT be enabled after enabling device
;         interrupts until after the "IRET".  You will lose atleast 12 bytes
;         on the stack for each recursion.

      cli
      mov      al,[int_no]                 ; Now reenable device interrupts
      call     unmaskint
      popf
      POP      bp di si es ds dx cx bx ax
      sti
      mov      cs:in_recv_isr, 0           ; clear the re-entrancy flag
      iret
recv_isr        ENDP


;--------------------
; parameter:
;        al : IRQ number
; register used:
;        ax, cx, dx
;--------------------
maskint PROC NEAR
      or       al, al              ;are they using a hardware interrupt?
      je       maskint_1           ;no, don't mask off the timer!
      mov      dx, MASKPORT        ;assume the master 8259.
      cmp      al, BASEVEC         ;using the slave 8259 on an AT?
      jb       mask_not_irq2
      mov      dx, MASKSLAVEPORT   ;go disable it on slave 8259
      sub      al, BASEVEC

mask_not_irq2:
      mov      cl,al
      in       al,dx               ;disable them on the correct 8259.
      mov      ah,1                ;set the bit.
      shl      ah,cl
      or       al,ah

        ;
        ;500ns Stall required here, per INTEL documentation for eisa machines
        ;
      push     ax
        ;in       al, KBEOI     ; 1.5 - 3 uS should be plenty
        ;in       al, KBEOI
        in       al, KBEOI
        out      KBEOI,al
        pop      ax

        out      dx,al                ; mask IRQ

maskint_1:
      RETN
maskint  ENDP


;--------------------
; parameter:
;        al : IRQ number
; register used:
;        ax, cx, dx
;--------------------
unmaskint       PROC NEAR

      mov      dx, MASKPORT         ;assume the master 8259.
      mov      cl, al
        cmp      cl, BASEVEC          ;using the slave 8259 on an AT?
      jb       unmask_not_irq2      ;no
      in       al, dx               ;get master mask
      and      al, NOT (1 shl 2)    ; and clear slave cascade bit in mask
      out      dx, al               ;set new master mask (enable slave int)

        ;
        ;500ns Stall required here, per INTEL documentation for eisa machines
        ;
      push     ax
      in       al, KBEOI            ; 1.5 - 3 uS should be plenty
        ;********in       al, KBEOI
        ;********in       al, KBEOI
        out      KBEOI,al             ; refresh keyboard stroke (LUTFI, 7/10/95)
        pop      ax
        mov      dx, MASKSLAVEPORT    ;go enable interrupt on slave 8259
        sub      cl, BASEVEC

unmask_not_irq2:
        in       al,dx                ;enable interrupts on the correct 8259.
        mov      ah,1                 ;clear the bit.
        shl      ah,cl
        not      ah
        and      al,ah

 ;    500ns Stall required here, per INTEL documentation for eisa machines
 ; --- not needed, caused a problem with kbhit(). (lutfi, april 1995)

        push          ax
        in          al, KBEOI            ; 1.5 - 3 uS should be plenty
        mov         ah, al               ; save keyboard stroke (LUTFI, 11/12/95)
        out         KBEOI, al            ; refresh keyboard stroke (LUTFI, 7/10/95)
        pop         ax

        out      dx,al                        ; un-mask IRQ

        RETN
unmaskint      ENDP

ENDS

END

;============================ END OF PROGRAM ===============================

Wednesday, December 16, 2009

iptables rules for Anti Social-Net on Linux

List of IP addresses used by most popular social-networking sites:

69.63.181.11, fb1
69.63.181.12, fb2
69.63.181.15, fb3
69.63.181.16, fb4
69.63.187.17, fb5
69.63.187.19, fb6
69.63.184.142, fb7
69.63.186.30, fb8
69.63.184.31, fb9
69.63.184.28, fb10
69.63.184.30, fb11
69.63.184.31, fb12
216.86.150.58, plurk
168.143.162.36, twitter

Pseudo code:
<foreach ip above> do {
       sudo iptables -A OUTBOUND -d <ip>/32 -j DROP
}

For firestarter, DROP is replaced with LSO

The above steps will create outbound firewall.

Tuesday, December 15, 2009

Best PC Hardwares in 2009

Mobo: Gigabyte GA-P55-UD6
Gaming Mouse: Logitech G9x
Liquid cooler: Corsair Cooling Hydro Series H5O
Mid-priced CPU: Intel Core i7-860
Video card: ATI Radeon HD-5870

Friday, December 11, 2009

Firewall made easy on Ubuntu

https://help.ubuntu.com/community/IptablesHowTo



Basic iptables howto

Iptables is a firewall, installed by default on all official Ubuntu distributions (Ubuntu, Kubuntu, Xubuntu). When you install Ubuntu, iptables is there, but it allows all traffic by default. Ubuntu 8.04 Comes with ufw - a program for managing the iptables firewall easily.
There is a wealth of information available about iptables, but much of it is fairly complex, and if you want to do a few basic things, this How To is for you.

Basic Commands

Typing
# iptables -L
lists your current rules in iptables. If you have just set up your server, you will have no rules, and you should see
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Basic Iptables Options

Here are explanations for some of the iptables options you will see in this tutorial. Don't worry about understanding everything here now, but remember to come back and look at this list as you encounter new options later on.
  • -A - Append this rule to a rule chain. Valid chains for what we're doing are INPUT, FORWARD and OUTPUT, but we mostly deal with INPUT in this tutorial, which affects only incoming traffic.
  • -L - List the current filter rules.
  • -m conntrack - Allow filter rules to match based on connection state. Permits the use of the --ctstate option.
  • --ctstate - Define the list of states for the rule to match on. Valid states are:
    • NEW - The connection has not yet been seen.
    • RELATED - The connection is new, but is related to another connection already permitted.
    • ESTABLISHED - The connection is already established.
    • INVALID - The traffic couldn't be identified for some reason.
  • -m limit - Require the rule to match only a limited number of times. Allows the use of the --limit option. Useful for limiting logging rules.
    • --limit - The maximum matching rate, given as a number followed by "/second", "/minute", "/hour", or "/day" depending on how often you want the rule to match. If this option is not used and -m limit is used, the default is "3/hour".
  • -p - The connection protocol used.
  • --dport - The destination port(s) required for this rule. A single port may be given, or a range may be given as start:end, which will match all ports from start to end, inclusive.
  • -j - Jump to the specified target. By default, iptables allows four targets:
    • ACCEPT - Accept the packet and stop processing rules in this chain.
    • REJECT - Reject the packet and notify the sender that we did so, and stop processing rules in this chain.
    • DROP - Silently ignore the packet, and stop processing rules in this chain.
    • LOG - Log the packet, and continue processing more rules in this chain. Allows the use of the --log-prefix and --log-level options.
  • --log-prefix - When logging, put this text before the log message. Use double quotes around the text to use.
  • --log-level - Log using the specified syslog level. 7 is a good choice unless you specifically need something else.
  • -i - Only match if the packet is coming in on the specified interface.
  • -I - Inserts a rule. Takes two options, the chain to insert the rule into, and the rule number it should be.
    • -I INPUT 5 would insert the rule into the INPUT chain and make it the 5th rule in the list.
  • -v - Display more information in the output. Useful for if you have rules that look similar without using -v.
  • -s --source - address[/mask] source specification
  • -d --destination - address[/mask] destination specification
  • -o --out-interface - output name[+] network interface name ([+] for wildcard)

Allowing Established Sessions

We can allow established sessions to receive traffic:
# iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
  • The above rule has no spaces either side of the comma in ESTABLISHED,RELATED
If the line above doesn't work, you may be on a VPS that uses OpenVZ or doesn't have some kernel extensions installed. In that case, try this line instead:
# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Allowing Incoming Traffic on Specific Ports

You could start by blocking traffic, but you might be working over SSH, where you would need to allow SSH before blocking everything else.
To allow incoming traffic on the default SSH port (22), you could tell iptables to allow all TCP traffic on that port to come in.
# iptables -A INPUT -p tcp --dport ssh -j ACCEPT
Referring back to the list above, you can see that this tells iptables:
  • append this rule to the input chain (-A INPUT) so we look at incoming traffic
  • check to see if it is TCP (-p tcp).
  • if so, check to see if the input goes to the SSH port (--dport ssh).
  • if so, accept the input (-j ACCEPT).
Lets check the rules: (only the first few lines shown, you will see more)
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
Now, let's allow all incoming web traffic
# iptables -A INPUT -p tcp --dport 80 -j ACCEPT
Checking our rules, we have
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
We have specifically allowed tcp traffic to the ssh and web ports, but as we have not blocked anything, all traffic can still come in.

Blocking Traffic

Once a decision is made to accept a packet, no more rules affect it. As our rules allowing ssh and web traffic come first, as long as our rule to block all traffic comes after them, we can still accept the traffic we want. All we need to do is put the rule to block all traffic at the end.

# iptables -A INPUT -j DROP
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
DROP       all  --  anywhere             anywhere
Because we didn't specify an interface or a protocol, any traffic for any port on any interface is blocked, except for web and ssh.

Editing iptables

The only problem with our setup so far is that even the loopback port is blocked. We could have written the drop rule for just eth0 by specifying -i eth0, but we could also add a rule for the loopback. If we append this rule, it will come too late - after all the traffic has been dropped. We need to insert this rule before that. Since this is a lot of traffic, we'll insert it as the first rule so it's processed first.

# iptables -I INPUT 1 -i lo -j ACCEPT
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
DROP       all  --  anywhere             anywhere
The first and last lines look nearly the same, so we will list iptables in greater detail.
# iptables -L -v

Chain INPUT (policy ALLOW 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:ssh
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:www
    0     0 DROP       all  --  any    any     anywhere             anywhere
You can now see a lot more information. This rule is actually very important, since many programs use the loopback interface to communicate with each other. If you don't allow them to talk, you could break those programs!

Logging

In the above examples none of the traffic will be logged. If you would like to log dropped packets to syslog, this would be the quickest way:
# iptables -I INPUT 5 -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
See Tips section for more ideas on logging.

Saving iptables

If you were to reboot your machine right now, your iptables configuration would disappear. Rather than type this each time you reboot, however, you can save the configuration, and have it start up automatically. To save the configuration, you can useiptables-save and iptables-restore.

Configuration on startup

WARNING: Iptables and NetworkManager seem to have a conflict. However NetworkManager is still in Beta. If you are concerned enough about security to install a firewall you might not want to trust NetworkManager to manage it yet. Also noteNetworkManager and iptables have opposite aims. Iptables aims to keep any questionable network traffice out.NetworkManager aims to keep you connected at all times. Therefore if you want security all the time, run iptables at boot time. If you want security some of the time then NetworkManager might be the right choice.
WARNING: If you use NetworkManager (installed by default on Feisty and later) these steps will leave you unable to useNetworkManager for the interfaces you modify. Please follow the steps in the next section instead.
NOTE: It appears on Hardy, NetworkManager has an issue with properly on saving and restoring the iptable rules when using the method in the next section. Using this first method appears to work. If you find otherwise, please update this note.
Save your firewall rules to a file
# iptables-save >/etc/iptables.rules
Then modify the /etc/network/interfaces configuration file to apply the rules automatically. You will need to know the interface that you are using in order to apply the rules - if you do not know, you are probably using the interface eth0, although you should check with the following command first to see if there are any wireless cards:
$ iwconfig
If you get output similiar to the following, then you do not have any wireless cards at all and your best bet is probably eth0.

$ iwconfig

lo        no wireless extensions.

eth0      no wireless extensions.

$
When you have found out the interface you are using, please open your /etc/network/interfaces file depending on what editor you want and/or what distribution you have:
Command line:
# nano /etc/network/interfaces
For Ubuntu and Xubuntu: type ALT+F2, then in the window that pops up, type:
gksudo gedit /etc/network/interfaces
and press Enter.
For Kubuntu: type ALT+F2, then in the window that pops up, type:
kdesu kate /etc/network/interfaces
and press enter.
When in the file, search for the interface you found, and at the end of the network related lines for that interface, add the line:
pre-up iptables-restore < /etc/iptables.rules
You can also prepare a set of down rules, save them into second file /etc/iptables.downrules and apply it automatically using the above steps:
post-down iptables-restore < /etc/iptables.downrules
A fully working example using both from above:

auto eth0
iface eth0 inet dhcp
  pre-up iptables-restore < /etc/iptables.rules
  post-down iptables-restore < /etc/iptables.downrules
You may also want to keep information from byte and packet counters.

iptables-save -c > /etc/iptables.save 
The above command will in other words save the whole rule-set to a file called /etc/iptables.save with byte and packet counters still intact.
Alternatively you could add the iptables-restore and iptables-save to the if-pre-up.d and if-post-down.d directories in the /etc/network directory instead of modifying /etc/network/interface directly.
The script /etc/network/if-pre-up.d/iptaload will contain:

#!/bin/sh
iptables-restore < /etc/iptables.rules
exit 0
and/etc/network/if-post-down.d/iptasave will contain:

#!/bin/sh
if [ -f /etc/iptables.downrules ]; then
   iptables-restore < /etc/iptables.downrules
fi
iptables-save -c > /etc/iptables.save
exit 0
Then be sure to give both scripts execute permissions:
# chmod +x /etc/network/if-post-down.d/iptasave
# chmod +x /etc/network/if-pre-up.d/iptaload

Configuration on Startup for NetworkManager

NetworkManager includes the ability to run scripts when it activates or deactivates an interface. To save iptables rules on shutdown, and to restore them on startup, we are going to create such a script. To begin, press Alt+F2 and enter this command:
For Ubuntu:
$ gksudo gedit /etc/NetworkManager/dispatcher.d/01firewall
For Kubuntu:
kdesu kate /etc/NetworkManager/dispatcher.d/01firewall
Then, paste this script into your editor, save, and exit the editor.
if [ -x /usr/bin/logger ]; then
        LOGGER="/usr/bin/logger -s -p daemon.info -t FirewallHandler"
else
        LOGGER=echo
fi

case "$2" in
        pre-up)
                if [ ! -r /etc/iptables.rules ]; then
                        ${LOGGER} "No iptables rules exist to restore."
                        return
                fi
                if [ ! -x /sbin/iptables-restore ]; then
                        ${LOGGER} "No program exists to restore iptables rules."
                        return
                fi
                ${LOGGER} "Restoring iptables rules"
                /sbin/iptables-restore -c < /etc/iptables.rules
                ;;
        post-down)
                if [ ! -x /sbin/iptables-save ]; then
                        ${LOGGER} "No program exists to save iptables rules."
                        return
                fi
                ${LOGGER} "Saving iptables rules."
                /sbin/iptables-save -c > /etc/iptables.rules
                ;;
        *)
                ;;
esac
Finally, we need to make sure NetworkManager can execute this script. In a terminal window, enter this command:
# chmod +x /etc/NetworkManager/dispatcher.d/01firewall

Tips


If you manually edit iptables on a regular basis

The above steps go over how to setup your firewall rules and presume they will be relatively static (and for most people they should be). But if you do a lot of development work, you may want to have your iptables saved everytime you reboot. You could add a line like this one in /etc/network/interfaces:
pre-up iptables-restore < /etc/iptables.rules
  post-down iptables-save > /etc/iptables.rules
The line "post-down iptables-save > /etc/iptables.rules" will save the rules to be used on the next boot.

Using iptables-save/restore to test rules

If you edit your iptables beyond this tutorial, you may want to use the iptables-save and iptables-restore feature to edit and test your rules. To do this open the rules file in your favorite text editor (in this example gedit).
$ sudo iptables-save > /etc/iptables.rules
$ gksudo gedit /etc/iptables.rules
You will have a file that appears similiar to (following the example above):
# Generated by iptables-save v1.3.1 on Sun Apr 23 06:19:53 2006
*filter
:INPUT ACCEPT [368:102354]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [92952:20764374]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A INPUT -j DROP
COMMIT
# Completed on Sun Apr 23 06:19:53 2006
Notice that these are iptables commands minus the iptable command. Feel free to edit this to file and save when complete. Then to test simply:
# iptables-restore < /etc/iptables.rules
After testing, if you have not added the iptables-save command above to your /etc/network/interfaces remember not to lose your changes:
# iptables-save > /etc/iptables.rules

More detailed Logging

For further detail in your syslog you may want create an additional Chain. This will be a very brief example of my /etc/iptables.rules showing how I setup my iptables to log to syslog:
# Generated by iptables-save v1.3.1 on Sun Apr 23 05:32:09 2006
*filter
:INPUT ACCEPT [273:55355]
:FORWARD ACCEPT [0:0]
:LOGNDROP - [0:0]
:OUTPUT ACCEPT [92376:20668252]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j LOGNDROP
-A LOGNDROP -p tcp -m limit --limit 5/min -j LOG --log-prefix "Denied TCP: " --log-level 7
-A LOGNDROP -p udp -m limit --limit 5/min -j LOG --log-prefix "Denied UDP: " --log-level 7
-A LOGNDROP -p icmp -m limit --limit 5/min -j LOG --log-prefix "Denied ICMP: " --log-level 7
-A LOGNDROP -j DROP
COMMIT
# Completed on Sun Apr 23 05:32:09 2006
Note a new CHAIN called LOGNDROP at the top of the file. Also, the standard DROP at the bottom of the INPUT chain is replaceed with LOGNDROP and add protocol descriptions so it makes sense looking at the log. Lastly we drop the traffic at the end of theLOGNDROP chain. The following gives some idea of what is happening:
  • --limit sets the number of times to log the same rule to syslog
  • --log-prefix "Denied..." adds a prefix to make finding in the syslog easier
  • --log-level 7 sets the syslog level to informational (see man syslog for more detail, but you can probably leave this)

Disabling the firewall

If you need to disable the firewall temporarily, you can flush all the rules using
# iptables -F
or create a script using text editor such as nano
# nano -w /root/fw.stop

echo "Stopping firewall and allowing everyone..."
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
Make sure you can execute the script
$ chmod +x /root/fw.stop
You can run the script
$ /root/fw.stop

Easy configuration via GUI

GUFW - Gufw is a graphical frontend to UFW (Uncomplicated Firewall).
A new user can use Firestarter (a gui), available in repositories (Synaptic or apt-get) to configure her/his iptable rules, without needing the command line knowledge. Please see the tutorial though... Configuration is easy, but may not be enough for the advanced user. However, it should be enough for the most home users... The (read:my) suggested outbound configuration is "restrictive", with whitelisting each connection type whenever you need it (port 80 for http, 443 for secure http -https-, 1863 for msn chat etc) from the "policy" tab within firestarter. You can also use it to see active connections from and to your computer... The firewall stays up once it is configured using the wizard. Dial-up users will have to specify it to start automatically on dial up in the wizard.
Homepage for firestarter: http://www.fs-security.com/ (again, available in repositories, no compiling required) Tutorial:http://www.fs-security.com/docs/tutorial.php