-
Notifications
You must be signed in to change notification settings - Fork 2
/
tcp.s
686 lines (650 loc) · 11.3 KB
/
tcp.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
include "zombie.def"
.area .data
flag rmb 1 ; user signal: 0 wait, 1 - closed, 2 - reset
retry rmb 1
hptr rmb 2
eport rmb 2 ; next ephemeral port no.
.area .code
;;; passive open
;;; for now, only one open
;;; takes: conn = socket
export tcp_listen
tcp_listen
ldx conn,pcr
;; set source port to zero
clr C_DPORT,x
clr C_DPORT+1,x
;; set new sequence to a random number
lbsr lfsr
ldd rand,pcr
std C_SNDN,x
lbsr lfsr
ldd rand,pcr
std C_SNDN+2,x
;; reset sync flag
clr flag,pcr
;; set timeout, callback, retries
leay listen_cb,pcr
sty C_CALL,x
;; spin until released
a@ tst flag,pcr
beq a@
rts
;;; active open
;;; takes: conn = socket
export tcp_connect
tcp_connect
lbsr initbuf
ldx conn,pcr
;; if ephemeral ports isn't set up
;; then set.
ldd eport,pcr
bne b@
ldd rand,pcr
ora #$c0
std eport,pcr
b@ ;; if our local port is 0 then pick an ephemeral port
ldd C_SPORT,x
bne c@
lbsr ephem
;; set new sequence to a random number
c@ lbsr lfsr
ldd rand,pcr
std C_SNDN,x
lbsr lfsr
ldd rand,pcr
std C_SNDN+2,x
;; set timeout, callback, retries
ldd #1*CPS ; time out
std C_TIME,x
leay cb_ssent,pcr
sty C_CALL,x
clr flag,pcr
ldb #3
stb retry,pcr
lbsr tcp_syn
;; sit and spin until released
a@ ldb flag,pcr
beq a@
ldb #1
cmpb flag,pcr
rts
;;; call-back for the listen state
export listen_cb
listen_cb
ldx hptr,pcr
ldy conn,pcr
cmpb #C_CALLTO
lbeq to@
;; is this a SYN?
ldb 13,x
bitb #2
beq out@ ; not a SYN
;; record ack + 1 for the sync
ldd 6,x
addd #1 ; add 1 to ack no for syn
std C_RCVN+2,y
ldd 4,x
adcb #0 ; add carry into MSB
adca #0
std C_RCVN,y
;; record dest port
ldd 0,x
std C_DPORT,y
;; record dest ip
ldd ripaddr,pcr
std C_DIP,y
ldd ripaddr+2,pcr
std C_DIP+2,y
;; set out timeout
ldx conn,pcr
ldd #1*CPS
std C_TIME,x
leay cb_ssent,pcr
sty C_CALL,x
ldb #3
stb retry,pcr
;; send out initial sync packet (from us)
lbsr ip_drop
lbsr tcp_syn
rts
to@ rts
out@ lbra ip_drop
ephem: pshs x
ldx conn,pcr ; stack conn ptr
pshs x
a@ lbsr for_sock ; start iterating
b@ lbsr next_sock
bcs out@
ldd eport,pcr
cmpd C_SPORT,x ; get src port of socket
bne b@ ; no then check next socket
addd #1 ; yes then try next port
bne s@ ; did we wrap to zero?
ldd #$c000 ; yes then start at beg of ephem ports
s@ std eport ; save in eport
bra a@ ; start socket scan afresh
out@ puls x ; restore conn ptr
stx conn,pcr
ldd eport,pcr
std C_SPORT,x ; save
addd #1
std eport,pcr
puls x,pc
pdebug
pshs a,x
lda [3,s]
ldx 3,s
leax 1,x
stx 3,s
jsr $a282
puls a,x,pc
;; callback for established state
export cb_estab
cb_estab
ldx hptr,pcr
ldy conn,pcr
cmpb #C_CALLTO
lbeq to@
;; check seq number if doesn't match ours
;; then drop it and ack for where we're at.
ldd 4,x ; check msb of seq
cmpd C_RCVN,y
lbne drop_and_ack
ldd 6,x ; check lsb of seq
cmpd C_RCVN+2,y
lbne drop_and_ack
inc flag,pcr
;; check ack: if this packet ackowledges our
;; queued send buffer then release it.
ldd C_SNDZ,y ; buffer empty?
beq d@
ldd 8,x
cmpd C_SNDN,y
bne d@
ldd 10,x
cmpd C_SNDN+2,y
bne d@
ldx C_SNDB,y
lbsr freebuff
clr C_SNDZ,y
clr C_SNDZ+1,y
;; then store data bytes (if any) from this buffer
d@ ldd #$0001 ; push ack / release buffer flags
pshs d
ldd pdulen,pcr ; get data bytes
beq b@
inc ,s
ldx pdu,pcr ; get data addr
lbsr add_to_ring ; D = # bytes xfered to ring
;; add bytes xfers to tcp ack no
ldd pdulen,pcr
addd C_RCVN+2,y
std C_RCVN+2,y
ldd C_RCVN,y
adcb #0
adca #0
std C_RCVN,y
;; is this a FIN packet?
b@ ldx hptr,pcr
ldb 13,x
bitb #1
beq a@
ldy conn,pcr
inc ,s ; set send ack flag
;; remember remote has closed
ldb #1
stb C_TFLG2,y
;; FIN bit counts as a sequence byte, so adjust answer by 1
ldd C_RCVN+2,y
addd #1
std C_RCVN+2,y
ldd C_RCVN,y
adcb #0
adca #0
std C_RCVN,y
a@ tst ,s+ ; send ack if needed
beq g@
lbsr tcp_ack
g@ tst ,s+ ; release buffer if needed
beq out@
lbra ip_drop
out@ rts
;; timeout received for this socket
;; if send buffer is full then resend it
;; if not then just reset timer
to@ ldd #1*CPS
std C_TIME,y
ldd C_SNDZ,y
lbne tcp_tx
lbra tcp_ack
rts
drop_and_ack
lbsr ip_drop
lbra tcp_ack
;; callback for active open, sync sent state
export cb_ssent
cb_ssent
cmpb #C_CALLTO
beq to@
ldx hptr,pcr
ldy conn,pcr
ldd 6,x
addd #1 ; add 1 to ack no for syn
std C_RCVN+2,y
ldd 4,x
adcb #0 ; add carry into MSB
adca #0
std C_RCVN,y
lbsr ip_drop
lbsr tcp_ack
ldy conn,pcr
ldd #cb_estab ; go to established state NOT PIC
std C_CALL,y
inc flag,pcr ; signal connected
rts
to@ dec retry,pcr
beq out1@
lbsr tcp_syn
ldx conn,pcr
ldd #1*CPS
std C_TIME,x
rts
out1@ inc flag,pcr ; signal closed to userspace
inc flag,pcr
rts
;;; send a syn
export tcp_syn
tcp_syn
lbsr getbuff
pshs x
leax 39,x ; todo: check size
pshs x
ldy conn,pcr
ldd C_SPORT,y ; append port nos
std ,x++
ldd C_DPORT,y
std ,x++
ldd C_SNDN,y ; our sequence number
std ,x++
ldd C_SNDN+2,y
std ,x++
;; inc our sequence no by one
addd #1
std C_SNDN+2,y
ldd C_SNDN,y
adcb #0
adca #0
std C_SNDN,y
;;
ldd #0
std ,x++ ; msb ack
std ,x++ ; lsb ack
ldd #$6002 ; data offset, syn
std ,x++
* ldd #BUFZ ; window
ldd bfree
std ,x++
ldd #0 ; cksum
std ,x++
ldd #0
std ,x++ ; urg ptr
ldd #$0204
std ,x++ ; option 2 mms
ldd #512
std ,x++ ; mms
ldb #6
stb proto,pcr
ldd C_DIP,y
std dipaddr,pcr
ldd C_DIP+2,y
std dipaddr+2,pcr
tfr x,d
subd ,s
puls x
lbsr tcp_cksum
lbsr ip_out
puls x
lbra freebuff
;;; send an ack
export tcp_ack
tcp_ack
a@ lbsr getbuff
bcs a@
pshs x
leax 39,x ; todo: check size
pshs x
ldy conn,pcr
ldd C_SPORT,y ; source port
std ,x++
ldd C_DPORT,y ; destination port
std ,x++
ldd C_SNDN,y ; msb seq
std ,x++
ldd C_SNDN+2,y ; lsb seq
std ,x++
ldd C_RCVN,y
std ,x++ ; msb ack
ldd C_RCVN+2,y
std ,x++ ; lsb ack
ldd #$5010 ; data offset, ack
std ,x++
* ldd #BUFZ ; window
ldd bfree
std ,x++
ldd #0 ; cksum
std ,x++
ldd #0
std ,x++ ; urg ptr
ldb #6
stb proto,pcr
ldd C_DIP,y
std dipaddr,pcr
ldd C_DIP+2,y
std dipaddr+2,pcr
tfr x,d
subd ,s
puls x
lbsr tcp_cksum
lbsr ip_out
puls x
lbra freebuff
;;; close socket
;;; takes: conn - socket ptr
export tcp_close
tcp_close
ldy conn,pcr
;; send FIN
ldb #1 ; set fin bit on empty packet
stb C_TFLG,y
ldx #0
ldd #0
lbsr tcp_send
;; wait for ack
clr flag,pcr
ldy conn,pcr
a@ tst flag,pcr
beq a@
;; reset callback + timeout
ldd #0
std C_CALL,y
rts
;;; received data
;;; takes: D - lenth of buffer, X - buffer ptr
;;; returns: D - lenth of data
;;; returns: C set on error
export tcp_recv
tcp_recv
pshs d,x,y,u
ldy conn,pcr
;; spin until there's data or closed/reset
a@ ldd #BUFZ
subd bfree
bne c@
tst C_TFLG2,y ; remote closed?
bne closed@
bra a@
;; copy data to ring buffer
c@ cmpd ,s
blo b@
ldd ,s
b@ lbsr rm_from_ring
std ,s
lbsr tcp_ack
puls d,x,y,u,pc
closed@ clr ,s
clr 1,s
puls d,x,y,u,pc
;;; send data
;;; takes: X ptr, D length
;;; takes: conn - socket ptr
;;; fixme: pushing extra unneccesary stuff on stack?
export tcp_send
tcp_send
pshs d,x
;; spin until socket's send buffer can be loaded
ldy conn,pcr
b@ ldd C_SNDZ,y
bne b@
;; spin until buffer is free
d@ lbsr getbuff
bcs d@
pshs x
leax 39,x ; todo: check size
pshs x
ldy conn,pcr
ldd 2,s ; save the packet buffer ptr
std C_SNDB,y
;; start filling out the packet
ldd C_SPORT,y ; source port
std ,x++
ldd C_DPORT,y ; destination port
std ,x++
ldd C_SNDN,y ; msb seq
std ,x++
ldd C_SNDN+2,y ; lsb seq
std ,x++
leax 4,x ; reserve room for ack (filled out later)
ldd #$5010 ; data offset, ack
orb C_TFLG,y ; or in additional flags
std ,x++
* ldd #BUFZ ; window (may want to send this in tcp_tx)
ldd bfree
std ,x++
ldd #0 ; cksum (filled out later)
std ,x++
std ,x++ ; urg ptr
;; cat data from application to packet
ldu 6,s
ldy 4,s
pshs x ; save start of tcp data
beq c@
a@ ldb ,u+
stb ,x+
leay -1,y
bne a@
;; next sequence no = sequence no + segment length
c@ ldy conn,pcr
tfr x,d
subd ,s++ ; subtract ptrs to get length
addd C_SNDN+2,y ; add to lsb of seq
std C_SNDN+2,y
ldd C_SNDN,y
adcb #0
adca #0
std C_SNDN,y
;; is a FIN packet then inc our next send by 1
ldb C_TFLG,y
bitb #1
beq e@
ldd C_SNDN+2,y
addd #1
std C_SNDN+2,y
ldd C_SNDN,y
adcb #0
adca #0
std C_SNDN,y
;; set ack timeout to 1 sec
ldd #1*CPS
std C_TIME,y
;; figure and set the packet's size
e@ tfr x,d
subd ,s
ldy conn,pcr
std C_SNDZ,y
leas 4,s
;; send it
lbsr tcp_tx
puls d,x,pc
tcp_tx
pshs y
ldy conn,pcr
ldx C_SNDB,y
leax 39,x
ldb #6
stb proto,pcr
ldd C_DIP,y
std dipaddr,pcr
ldd C_DIP+2,y
std dipaddr+2,pcr
ldd C_RCVN,y
std 8,x
ldd C_RCVN+2,y
std 10,x
ldd C_SNDZ,y
lbsr tcp_cksum
lbsr ip_out
puls y,pc
;; todo: precalculate the relatively static
;; calc pseudo-header
;; takes: X
tcp_cksum
pshs d,x,y
clr 16,x ; clear out old cksum
clr 17,x
ldd ipaddr,pcr
addd ipaddr+2,pcr
adcb #0
adca #0
ldy conn,pcr
addd C_DIP,y
adcb #0
adca #0
addd C_DIP+2,y
adcb #0
adca #0
addb #6
adca #0
addd ,s
adcb #0
adca #0
ldy ,s
lbsr ip_cksum
std 16,x
a@ puls d,x,y,pc
export tcp_in
tcp_in
lbsr for_sock
a@ lbsr next_sock
lbcs ip_drop ; fixme: send reset here
ldy conn,pcr
ldb C_FLG,y ; is a TCP socket?
cmpb #C_TCP
bne a@
ldd 2,x ; is packet's dest port our source port?
cmpd C_SPORT,y
bne a@
ldd C_DPORT,y ; is socket listening?
beq go@
cmpd ,x ; is packet's source port our dest port?
bne a@
;; fixme: filter for anything else here? (cksum?)
;; found our socket
;; record pdu / length
go@ stx hptr,pcr ; save the header pointer
ldb 12,x ; data offset
lsrb ; multiply by 4 - header size in bytes
lsrb ; (its in top nibble.. so divide by 4 rather)
leay b,x ; add header length to packet base
sty pdu,pcr ; and store address to pdu
ldy rlen,pcr ; get the length
negb ; subtract length from ip's length
leay b,y ;
sty pdulen,pcr ; save the length of the tcp segment
;; check for tcp reset
ldb 13,x
bitb #4
bne tcp_reset
;; call the callback
ldx conn,pcr
ldx C_CALL,x
lbeq ip_drop
ldb #C_CALLRX
jmp ,x
tcp_reset:
ldx conn,pcr ; get connection ptr
ldb #3
stb flag,pcr ; set reset flag to userspace
ldd #0
std C_CALL,x
lbra ip_drop
;;; initialize the tcp subsystem
export tcp_init
tcp_init
clr eport,pcr
clr eport+1,pcr
rts
BUFZ equ 1024
buf rmb BUFZ
bfree .dw BUFZ
biptr .dw buf
boptr .dw buf
initbuf ldd #BUFZ
std bfree
leax buf,pcr
stx biptr
stx boptr
rts
;;; add data to ring buffer
;;; x = data ptr
;;; d = size
;;; returns: d = bytes copied
;;; fixme: make this better by calculating memory cpy sizes
;;; fixme: make PIC
;;; fixme: put vars in socket structure
add_to_ring:
pshs cc
orcc #$50
pshs d,x,y,u
;; find minimum of buffer and passed size
cmpd bfree
blo a@
ldd bfree
a@ std ,s
beq c@
tfr d,y
ldu biptr
b@ lda ,x+
sta ,u+
cmpu #buf+BUFZ
bne d@
ldu #buf
d@ leay -1,y
bne b@
ldd bfree
subd ,s
std bfree
stu biptr
c@ puls d,x,y,u
puls cc,pc
;;; unqueue data from ring buffer
;;; x = data ptr
;;; d = no of bytes
;;; returns: D = number of bytes copied
rm_from_ring
pshs cc
orcc #$50
pshs d,x,y,u
ldd #BUFZ
subd bfree
cmpd ,s
blo a@
ldd ,s
a@ std ,s
beq c@
tfr d,y
ldu boptr
b@ lda ,u+
sta ,x+
cmpu #buf+BUFZ
bne d@
ldu #buf
d@ leay -1,y
bne b@
;; adjust size, ptr of buffer
ldd bfree
addd ,s
std bfree
stu boptr
c@ puls d,x,y,u
puls cc,pc